5.SpringIOC自动注入
5.1.Spring容器管理注解
Spring自动扫描,会把以下注解的bean纳入Spring容器管理
@Component不好分层时用该注解
@Controller控制层使用该注解
@Service业务层使用该注解
@Repository dao层使用该注解
5.2.AutoWired注入
- 添加context命名空间,1拖2
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" //xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd //http://www.springframework.org/schema/context //https://www.springframework.org/schema/context/spring-context.xsd "> </beans>
- applicationContext.xml文件配置扫描基本包
<context:component-scan base-package="com.tjetc"></context:component-scan>
- @AutoWired自动注入
@Repository public class UserDao { public void login(){ System.out.println("用户登录"); } }
@Service public class UserService { @Autowired private UserDao userDao; public void login(){ userDao.login(); } }
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService bean = context.getBean(UserService.class); bean.login();
运行结果
用户登录
5.3.接口多个实现类的注入方式
- 准备UserDao接口,和两个实现类
public interface UserDao { void login(); }
@Repository public class UserMySqlDaoImpl implements UserDao { public void login() { System.out.println("UserMySqlDaoImpl.login()"); } }
@Repository public class UserOracleDaoImpl implements UserDao { public void login() { System.out.println("UserOracleDaoImpl.login()"); } }
- 注意在UserService注入的时候,要在@Autowired下写@Qualifer(“bean的名字”)注解
@Service public class UserService { @Autowired @Qualifier("userMySqlDaoImpl") //指定是哪个实现类 private UserDao userDao; public void login(){ userDao.login(); } }
- 测试代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService bean = context.getBean(UserService.class); bean.login();
- 运行结果
UserMySqlDaoImpl.login()
5.4.Spring的5种自动装配模式
no:默认情况,不自动装配,手动设置bean
byName:根据bean 的名字自动装配
byType:根据bean的数据类型自动装配
constructor:根据构造函数的参数的数据类型,进行byType模式的自动装配
autodetect:如果发现默认的构造函数,用constructor模式,否则,用byType模式
- 准备实体类Person、Customer
public class Customer { private Person person; public Customer(Person person) { System.out.println("调用构造注入"); this.person = person; } public Customer() { } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } @Override public String toString() { return "Customer{" + "person=" + person + '}'; } }
public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } }
- 配置applicationContext.xml
no方式
<bean id="person" class = "com.tjetc.domain.Person"> <property name="name" value="李祥"></property> </bean> <bean id="customer" class="com.tjetc.domain.Customer" autowire="no"> <property name="person" ref="person"></property> </bean>
byName方式
<bean id="person" class = "com.tjetc.domain.Person"> <property name="name" value="李祥"></property> </bean> <bean id="customer" class="com.tjetc.domain.Customer" autowire="byType"> </bean>
byType方式
<bean id="person" class = "com.tjetc.domain.Person"> <property name="name" value="李祥"></property> </bean> <bean id="customer" class="com.tjetc.domain.Customer" autowire="byType"> </bean>
constructor方式
<bean id="person" class = "com.tjetc.domain.Person"> <property name="name" value="李祥"></property> </bean> <bean id="customer" class="com.tjetc.domain.Customer" autowire="constructor"> </bean>
5.5.注入方式总结
6.Spring Bean的管理
6.1Bean的scope属性
(1)singleton单例(默认)
(1)singleton单例(默认)
当scope的取值为singleton时 Bean的实例化个数:1个 Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“applicationContext.xml”);
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="singleton">
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao userDao1 = (UserDao) app.getBean("userDao"); UserDao userDao2 = (UserDao) app.getBean("userDao"); System.out.println(userDao1); System.out.println(userDao2);
打印的userDao1、userDao2地址相同
(2)prototype多例
Bean的实例化个数:多个 Bean的实例化时机:当调用getBean()方法时实例化Bean
对象创建:当使用对象时,创建新的对象实例
UserDao userDao1 = (UserDao) app.getBean(“userDao”);
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="prototype">
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao userDao1 = (UserDao) app.getBean("userDao"); UserDao userDao2 = (UserDao) app.getBean("userDao"); System.out.println(userDao1); System.out.println(userDao2);
打印的userDao1、userDao2地址不同
(3)request ,session和global session
这三个类型是spring2.0之后新增的,他们不像singleton和prototype那么通用,因为他们只适用于web程序,通常是和XmlWebApplicationContext共同使用
<bean id ="requestPrecessor" class="...RequestPrecessor" scope="request" />
Spring容器,即XmlWebApplicationContext 会为每个HTTP请求创建一个全新的RequestPrecessor对象,当请求结束后,,该对象的生命周期即告结束。当同时有10个HTTP请求进来 的时候,容器会分别针对这10个请求创建10个全新的RequestPrecessor实例,且他们相互之间互不干扰,从不是很严格的意义上 说,request可以看做prototype的一种特例,除了场景更加具体之外,语意上差不多。
<bean id ="userPreferences" class="...UserPreferences" scope="session" />
Spring容器会为每个独立的session创建属于自己的全新的UserPreferences实例,他比request scope的bean会存活更长的时间,其他的方面真是没什么区别。
<bean id ="userPreferences" class="...UserPreferences" scope="globalsession" />
global session只有应用在基于porlet的web应用程序中才有意义,他映射到porlet的global范围的session,如果普通的servlet的web 应用中使用了这个scope,容器会把它作为普通的session的scope对待。
6.2.Spring bean生命周期回调
(1)xml文件配置方式
- 准备一个被spring容器管理的类
public class A { public A(){ System.out.println("A()..."); } public void init(){ System.out.println("A.init()..."); } public void destroy(){ System.out.println("A.destroy()..."); } }
- 在配置文件里配置bean、初始化方法、销毁方法
<bean id="a" class="com.tjetc.domain.A" init-method="init" destroy-method="destroy"></bean>
- 测试代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); A bean = context.getBean(A.class); System.out.println(bean); context.close();
- 运行结果
A()... A.init()... com.tjetc.domain.A@66a3ffec A.destroy()...
(2)JSR250注解@PostConstruct@PreDestroy
- spring容器管理的类加上@Component,在初始化方法上加上@PostConstruct注解,在销毁方法上加上@PreDestroy注解
@Component public class A { public A(){ System.out.println("A()..."); } @PostConstruct public void init(){ System.out.println("A.init()..."); } @PreDestroy public void destroy(){ System.out.println("A.destroy()..."); } }
- applicationContext.xml
<context:component-scan base-package="com.tjetc"></context:component-scan>
- 测试代码不变
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); A bean = context.getBean(A.class); System.out.println(bean); context.close();
- 运行结果
A()... A.init()... com.tjetc.domain.A@44a3ec6b A.destroy()...
(3)接口InitializingBean和DisposableBean
- spring容器管理的类要实现InitializingBean和DisposableBean接口
@Component public class A implements InitializingBean, DisposableBean { public A(){ System.out.println("A()..."); } public void afterPropertiesSet() throws Exception { System.out.println("A.afterPropertiesSet()..."); } public void destroy() throws Exception { System.out.println("A.destroy()..."); } }
- applicationContext.xml配置基本扫描包
<context:component-scan base-package="com.tjetc"></context:component-scan>
- 测试代码不变
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); A bean = context.getBean(A.class); System.out.println(bean); context.close();
- 运行结果
A()... A.afterPropertiesSet()... com.tjetc.domain.A@4516af24 A.destroy()...
6.3.钩子关闭IOC容器
- 使用钩子,在非WEB环境下,可以优雅的关闭IOC容器。
- 如富客户端的桌面环境,可以向JVM注册一个钩子。即使程序非正常退出,钩子函数也会被执行,这样在钩子函数中做环境清理工作,如关闭非托管资源,就是非常有效的方法。
注册一个shutdown hook,需要调用ConfigurableApplicationContext接口中的registerShutdownHook()方法。
@Test public void testHook() throws Exception { Runtime.getRuntime().addShutdownHook(new Thread(){//2. @Override public void run() { System.out.println("钩子函数清除垃圾"); } }); ConfigurableApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); context.registerShutdownHook();//1.在容器关闭时使用上面的钩子函数释放资源 context.close(); System.out.println("我要正常关闭了"); }
6.4.BeanPostProcessor接口
bean 的后置处理接口
IOC容器生成bean对象后,在bean初始化前后,你可以通过BeanPostProcessor接口定制你的业务逻辑,如日志跟踪等。
配置BeanPostProcessor后Bean的使用过程如下:
- 写一个类实现InitializingBean, DisposableBean接口
@Component public class A implements InitializingBean, DisposableBean { public A(){ System.out.println("A()..."); } public void afterPropertiesSet() throws Exception { System.out.println("A.afterPropertiesSet()..."); } public void destroy() throws Exception { System.out.println("A.destroy()..."); } }
- 写一个类LogBean实现BeanPostProcessor接口,重写2个方法
@Component public class LogBean implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("LogBean.postProcessBeforeInitialization():"+bean+":"+beanName); return null; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("LogBean.postProcessAfterInitialization():"+bean+":"+beanName); return null; } }
- 测试代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); A bean = context.getBean(A.class); System.out.println(bean); context.close();
运行结果
A()... LogBean.postProcessBeforeInitialization():com.tjetc.domain.A@10dba097:a A.afterPropertiesSet()... LogBean.postProcessAfterInitialization():com.tjetc.domain.A@10dba097:a com.tjetc.domain.A@10dba097 A.destroy()...
6.5.FactoryBean接口
- FactoryBean就是对一个复杂Bean的包装,可以在FactoryBean中进行初始化,然后把初始化的值传给它包装的对象。
- FactoryBean接口在Spring framework框架自身,有大量的实现,如用于创建动态代理对象的ProxyFactoryBean。
- 实现FactoryBean中的getObject()方法,返回真正需要的对象。
- 首先准备一个类,用于存放数据库连接属性
public class JDBCTest { private String url; private String username; private String password; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
- 加入maven依赖,mysql,spring-jdbc
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.8.RELEASE</version> </dependency>
配置数据源和基本扫描包在applicationContext.xml
<context:component-scan base-package="com.tjetc"></context:component-scan> <bean id = "dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql:///test"></property> <property name="username" value="root"></property> <property name = "password" value="123456"></property> </bean>
- 写一个工厂类实现FactoryBean,InitializingBean接口,在初始化中完成属性的赋值
@Component public class MyFactoryBean implements FactoryBean<JDBCTest>, InitializingBean { @Autowired private DriverManagerDataSource dataSource; private JDBCTest jdbcTest; public JDBCTest getObject() throws Exception { return jdbcTest; } public Class<?> getObjectType() { return JDBCTest.class; } public boolean isSingleton() { return true; } public void afterPropertiesSet() throws Exception { Connection connection = dataSource.getConnection(); System.out.println("链接对象为:"+connection); jdbcTest = new JDBCTest(); jdbcTest.setUrl(dataSource.getUrl()); jdbcTest.setUsername(dataSource.getUsername()); jdbcTest.setPassword(dataSource.getPassword()); } }
- 测试代码
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); JDBCTest jdbcTest = (JDBCTest) context.getBean("myFactoryBean"); System.out.println(jdbcTest.getUrl()); System.out.println(jdbcTest.getUsername()); System.out.println(jdbcTest.getPassword());
- 运行结果
链接对象为:com.mysql.jdbc.JDBC4Connection@4b168fa9 jdbc:mysql:///test root 123456
6.6.JSR注解
(1)Spring与JSR330对应注解
Spring | javax.inject.* |
@Autowrid | @Inject |
@Component | @Named/@ManagedBean |
@Scope(“singleton”) | @Singleton |
@Qualifier | @Qualifier/@Named |
@Value | — |
@Required | — |
@Lazy | — |
ObjectFactory | Provider |
(2)@Inject 是JSR 330的注解,在使用@Autowired地方,可以使用 @Inject代替
@Service public class UserService { @Inject private UserDao userDao; public void login(){ userDao.login(); } }
(3)@Named或者@ManagedBean,代替Component
@Named //@ManagedBean(需要javaee7.0 maven依赖) public class MyFactoryBean implements FactoryBean<JDBCTest>, InitializingBean { @Autowired private DriverManagerDataSource dataSource; private JDBCTest jdbcTest; public JDBCTest getObject() throws Exception { return jdbcTest; } public Class<?> getObjectType() { return JDBCTest.class; } public boolean isSingleton() { return true; } public void afterPropertiesSet() throws Exception { Connection connection = dataSource.getConnection(); System.out.println("链接对象为:"+connection); jdbcTest = new JDBCTest(); jdbcTest.setUrl(dataSource.getUrl()); jdbcTest.setUsername(dataSource.getUsername()); jdbcTest.setPassword(dataSource.getPassword()); } }
(4)@Resource代替@Inject、@Autowired
@Resource可以应用在属性、Set方法上,注入数据。
使用@Resource代替@Inject、@Autowired
与@Autowired相反,@Resource默认的装配方式是byName
如果不写name属性,用@Resource按名称装配,如果找不到就回退到按类型装配
如果写name属性,用@Resource(name=“userDaoMysql”)按名称装配,如果找不到就不能回退到按类型装配了
@Service public class UserService { @Resource private UserDao userDao; public void login(){ userDao.login(); } }
(5)@Required
必须输入,只能用在属性setter,配置文件配置,不推荐使用
(6)@Configuration @Bean
Configuration //配置类,相当于applicationContext.xml
@Bean //
@Configuration public class MyConfig { @Bean public UserDao userDao(){ return new UserMySqlDaoImpl(); } }
(7)@Primary
多个相同对象使用@Primary优先采用哪一个对象
@Repository @Primary public class UserOracleDao implements UserDao { @Override public void login() { System.out.println("UserOracleDao.login()..."); } }