欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

什么是springIOC什么是springAOP?

程序员文章站 2022-06-04 15:13:25
...

Spring IOC的原理概述
创建对象是十分熟悉的并且是十分繁琐的,每次创建对象都要亲自new出来,甚至在某些情况下因为使用坏的编程习惯还会造成对象无法回收,松耦合少侵入原则进行编程

上面很大程度上降低了代码的耦合度,但实际上在修改实现类的时候,还是要选择修改BookServiceImpl中的内容,因为这里面创建了很多的实例,因此这种情况下还是存在很多耦合的情况,如何降低耦合是要解决的问题

如何降低耦合,代码中出现了创建对象的情况?

代码中出现了创建对象的情况,可以通过反射的方式解决这个问题,反射是根据给出的完整的类名(字符串)动态的生成对象,让对象在生成的时候才决定到底是哪一种对象。
在某个配置文件中,写好了bookDaoImpl类的完全限定名,通过读取该文件而获取到bookDao的真正实现类的完全限定名,然后通过反射的方式在运行的时候动态的生成该类,最终赋值给bookDao接口,这样就解决了刚才存在的问题。
SpringIOC也是一个java对象,在某些特定的事件被创建之后,可以对其他对象进行初始化,创建,销毁等等,在上述过程种,我们通过配置文件配置了BookDao实现类的完全限定名称,然后利用反射在运行时候为BookDao创建实际的实现类,包括BookServiceImpl的创建按,Spring的ioc容器都会帮助完成,而我们要做的事情就是把需要创建的类和类依赖的类以配置问及那的方式告诉IOC容器需要创建哪些类,注入哪些类,Spring通过这种ioc的设计模式实际上促进了松耦合这总方式使一个对象依赖其他对象的时候,会通过被动的方式传送进来,BookServiceImpl被创建的时候,其依赖的BookDao的实现类同时也会被注入到Bookimpl种而不是手动创建这些类,我们可以把IOC模式看成工厂模式的升华,可以把IOC容器看成一个大的工程,这个大的工厂了要生成的对象都是在配置文件中给出定义的,然后通过java的反射技术根据xml中给出的类名生成相应对象。将工厂方式中的硬编码创建对象的代码改编为由xml文件来定义,就是说把工厂和对象两者分开,提高了灵活性和可维护性,ioc就是将对象的创建权交给spring完成,从此解放手动创建对象的过程。

Spring案例

理解了springIOC容器之后,看一个简单的实例,使用SpringIOC功能后首先要引入Spring的核心依赖包:

maven中添加依赖
Spring-core
spring-beans
Spring-context

dao层接口
public  interface AccountDao{
	void addCount();
}

dao层实现类,一般情况下不用实现dao层的接口,而是用来做查询sql语句的
public class AccountDaoImpl implements AccountDao{
	@override
	public void Acccount(){
		System.out.println("addAcount.....");
	}
}
  
service层接口,该接口一般会在控制层中创建实例
public interface AccountService{
	void doSomething();
}

服务层的实现类:
public class AccountServiceImpl implements AccountService{
	private AccountDao accountDao;
	public void SetAccountDao(AccountDao accountDao){
		this.accoutDao=accountDao;
	}
	@Override
	public void  doSomething(){
		System.out.println("AccountServiceimpl doSometing.....");
		accountDao.addAccount();
	}
}
从创建了Dao层和Service层的接口类以及实现类,其中Service层的操作依赖于Dao层,通过Sprng的IOC容器帮助我们创建并注入这些类,使用的使XML文件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd ">
	<!--声明一个对象,对象的实现类是dao.impl.AccountDaoImpl 
	<bean name="accountDao" class="com.spring.springioc.dao.impl.AccountDaoImpl"/>
	<!--声明一个对象,对象的实现类是AccounntSericeimpl>
	<bean neme="accoutService" class="com.spring.service.impl.AccountServiceImpl"/>
		<!--注入accountDao对象,使用里面的set方法-->
		<property name="accountDao" ref="accountDao"/>
	</bean>
</beans>

xml文件中要声明一个beans的*标签,同时要引入核心命名空间,Spring的功能在使用的时候要声明相应的命名空间。

通过bean标签声明哪些需要ioc容器帮我们创建的类,其中name是指明IOC容器创建对象后该对象的名称,也可以使用id替换name,class是告诉这个类的完全限定名,IOC会通过这组信息利用反射技术创建对象,第二个accoutService对象中有一个property属性,这个属性就是引用了这个对象,作用是将引用的对象传递给accountDao属性,这个属性必须通过set方法才能注入进入,配置文件中的名字必须与set方法中的变量名称相同,这样就完成了对要创建的对象的声明

通过配置文件使用bean的实例

@Test
public void testByXml() throws Exception{
	AppliactionContext applicationContext =new ClassPathXmlApplicationContext("/spring/spring-ioc.xml");
	//上面已经读取了该配置文件
	//AccountService accountService=applicationContext.getBean("accountService“,AccountService.class);
	//多次获取并不会创建多个accountService对象,因为Spring默认创建的是但实例的作用域
	AccoutService accountService=(AccoutService)appliactionContext.getBean("accountService");
}

使用这些类的时候使用Spring提供的核心类ApplicationContext去加载已经声明好的配置文件,这样就可以获取到需要的类

Spring IOC容器装配Bean有几种方式?

  1. 通过xml文件进行装配
  2. 采用注解的方式进行配置

通过xml文件进行装配

采用xml配置文件的方式对bean进行声明和管理,每个bean标签标识需要被创建的对象,并且在bean标签中通过该类可以为该类注入其他依赖对象,通过这种方式spring容器就知道我们需要创建那些容器,并进行管理对象之间的依赖关系。
使用ClassPathXmlApplicationContext去加载spring的配置文件,接着获取想要的实例Bean并调用相应的方法进行执行,ClassPathXmlApplicationContext会加载classpath路径下的文件,只要制定相应的路径就行,如果存在多个配置文件,分别传递或者是传递一个数组。实际上除了ClassPathXmlApplicationContext外FileSystemXmlApplicaitonContext也可以使用,默认项目共做路径为读取的文件路径

通过注解的形式进行配置

使用注解的方式进行配置
要导入两个包
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration  //添加Configuration注解
public class BeanConfiguration{
	
	@Bean
	public AccountDao accountDao(){
		return new AccountDaoImpl(); // 实现类是AccountDaoImpl这个类,实现类就是这个类
	}
	@bean
	public AccountService accountService(){
		AccountServiceImpl bean=new AccountServiceImpl(); // 实现类指的是这个类,AccountServiceImpl
		bean.setAccountDao(accountDao()); // 将另一个实例注入到这个bean中,等价于property模块
		return bean;
	}
}

上面使用@Configuration注解表示BeanConfiguration类,表示这个类就是代替了xml文件,也就是说注解@Configuration相当于标签,每个@Bean对应的就是xml文件中的bean标签,xml文件中的property标签表示将一个实例注入进去。使用注解方式需要在maven中依赖spring-context包

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>${spring.version}</version>
<dependency>

通过注解方式创建bean的使用实例

@Test
public void testByConfigurationAnnotation() throws Exception{
	AnnotationConfigApplicationContext config=new AnnocationConfigApplicationContext(BeanConfiguration.class); // 在这里获取类名
	AccountService accountService=(AccountService)config.getBean("accountService");
	accountService.doSomething();
}

通过xml配置文件和通过注解配置的方式二者实际上是一样的,只是获取配置文件的方式不同:

AppliactionContext applicationContext =new ClassPathXmlApplicationContext("/spring/spring-ioc.xml");
AnnocationConfigApplicationContext config=new AnnocationConfigApplicationContext(BeanConfigration.class)

**总结:**实际上使用xml还是注解的方式都是一样的,但是大部分情况下,更加的倾向于使用xml文件来配置bean因为这种情况下适合对代码进行管理,在xml文件中除了使用标签为每一个类声明实例之外,Spring容器还提供了基于注解的声明方式

依赖注入功能

spring的依赖注入功能,实际上就是当一个bean实例引用了另一个bean实例的时候,spring容器帮助我们创建依赖bean实例并注入到另一个bean中,就想上面的AccountService依赖于AccountDao,spring容器会在创建AccountService的实现类和AccountDao的实现类后,把AccountDao的实现类注入到AccoountService实例中

Setter注入

Setter注入,被注入的类的属性中有时候需要set方法,Setter注入支持简单类型和引用类型,Setter注入是在bean实例创建完成之后执行的,直接观察前面的案例,对象注入使用的ref属性
除了使用上述的对象注入的同时还可以注入简单值和map set list 数组,简单值使用的value属性,
对应的程序代码是:

public class Account {
    private String name;
    private String pwd;
    private List<String> citys;
    private Set<String> friends;
    private Map<Integer,String> books;

    public void setName(String name) {
        this.name = name;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public void setCitys(List<String> citys) {
        this.citys = citys;
    }

    public void setFriends(Set<String> friends) {
        this.friends = friends;
    }

    public void setBooks(Map<Integer, String> books) {
        this.books = books;
    }
}

对应的xml文件中的配置,普通类型使用value,对象类型使用class

<bean id=”account" scope="prototype" class="com.spring.springIOC.pojo.Account">
	<property name="name" value="I am SpringIOC“/>  这里表示注入普通值
	<property name="password" value="123“/>
	<property name="books”>
		<map>
			<entry key="10" value="Spring-core"></entry>
		  <entry key="11" value="spring-beans"></entry>
		  <entry key="12" value="spring-context“></entry>
	   </map>
	<property>

	<property name=”friends"> 这里表示注入的是set集合
		<set>
				<value>一</value>
				<value> 二</value>
	    </set>
	</property>
	<property name="citys">
		<list>
			<value>上海</value>
			<value>北京</value>
		</list>
	</property>
</bean>

通过构造函数注入
通过构造方法注入依赖,构造函数的参数一般情况下就是依赖项,spring容器会根据bean中指定的构造函数参数来决定调用哪个构造函数

public class AccountServiceImpl implements AccountService{

	private AccountDao accountDao;	// 需要注入的对象 Dao层的对象
	@param accountDao   // 构造注入
	public AccountServiceImpl(AccountDao accountDao){
		this.accountDao=accountDao;
	}	
}

对应的xml配置文件为:
<bean name=“accountDao” clas="com.spring.springioc.dao.impl.AccountDaoImpl/>



可以看出,通过构造函数注入和通过setter进行注入是一样的,setter注入使用的是property属性,构造注入也可以传入简单值类型和集合类型和对象类型,注入的每一个值都有一个标签,因此标签中可以有多个constructor标签,并且他们的顺序不重要,spring容器会通过配置文件中传入的依赖参数与其中的构造函数进行比较尝试找到合适的构造函数。但是这里存在一个问题,但参数个数和参数都是一样的,只是顺序不同,这在class中的定义是可以的,但是对于springIOC容器来说是不行的,如:

public class User {

    private String name;
    private int age;

    //第一个构造函数
    public User(String name , int age){
        this.name=name;
        this.age=age;
    }
    //第二个构造函数
    public User(int age,String name){
        this.name=name;
        this.age=age;
    }
}
如果我们对应的xml配置信息为
<bean id="user" class="com.spring.springioc.pojo.user“>
	<constructor-arg type="java.lang.String" value=”Jack"/>
	<constructor-arg type="int" value="26"/>
</bean>

当程序运行的时候,spring容器会查找合适的user构造函数进而创建user对象,由于的构造顺序重要,因此spring容器不知道应该使用这两种user构造函数的哪一种,这个时候实例将要创建失败,spring容器也将会启动失败,但是比较好的是,Spring已经知道会出现这种情况,这个时候只要给spring容器一点提示就可以成功的找到合适的构造安徽念书从而创建user实例,构造函数中的index属性,通过index属性可以告诉spring容器传递的依赖参数的顺序,如

<constructor-arg index="0“ value=“jack”/>