spring
全家桶依赖管理
全家桶依赖通过artifactId区分,有tx、beans、aspect等支持,本文以5.2.12为验证版本,下为核心依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframe.version}</version>
</dependency>
依赖注入&控制反转
思想很简单,本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的,这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转(Bean托管给Spring容器)。
形式上:将依赖对象的实例化交给其调用者,通过某种方式传入进来。
思想上:仔细理解的话可以发现这种托管比工厂还要方便,根据诉求直接拿出来,用户不关心甚至不感知工厂的存在。
Bean的理解
理论上,符合JavaBean规范的类和不符合JavaBean规范的POJO都可以配置成Bean,也就是说,基本上所有的类都可以配置成Bean并交由Spring控管。容器使用反射机制来创建对象和注入依赖,过度地使用对系统的性能会产生不必要的浪费。对于细粒度的域对象,类似于实体类就没有必要交由容器管理。从类实现的功能上看,交由容器控管的对象主要包括以下几种:
服务层对象:包括桌面应用中的逻辑功能类,以及Web MVC应用中的控制类、服务类。
数据访问对象:和数据库进行操作,对数据进行增、删、改、查的类对象及事务处理的相关类对象。
框架基础对象:例如框架用于注解支持的类和持久化框架整合的基础对象等。
Spring容器主要是对对象的生命周期和对依赖关系进行管理。从类的特征上看,具备单例特性的类都适合交由容器管理、依赖。
基本使用
BeanFactory实现
ApplicationContext和XmlBeanFactory两种实现方式,通过classpath读取xml需在idea中将文件夹标记为Resource,前者更易用且继承了更多接口,具备了事件发布等额外功能,后者对于自定义BeanPostProfessor时需要显示绑定。
package spring.beanfactory;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import spring.bean.Person;
/**
* BeanFactory为基础接口
* 常用的实现类XmlBeanFactory,5.2.12已结被标记废弃了
* ApplicationContext从BeanFactory为继承两轮得到,基础实现类为ClassPathXmlApplicationContext
*/
public class BeanFactoryTest {
public static void main(String[] args){
//XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
//Person p = (Person) beanFactory.getBean("person");
//System.out.println(JSONObject.toJSONString(p));
//入参支持数组、同时加载多个xml
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person t = (Person) context.getBean("person2");
System.out.println(JSONObject.toJSONString(t));
Action action = (Action) context.getBean("action");
System.out.println(JSONObject.toJSONString(action));
}
}
xml注册bean方式
xml文件,注意头文件编写,默认bean都是单例的,除非明确需要prototype类型。
默认bean注册是不开启autowire的,即=no。如下为示例:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 通过getter、setter方法注入 -->
<bean id="pat" class="spring.bean.Animals" scope="singleton">
<property name="type" value="cat"></property>
</bean>
<!-- 通过ref方法注入 -->
<bean id="person" class="spring.bean.Person" init-method="myInit"
destroy-method="myDestroy" scope="singleton">
<property name="name" value="bob">
<!-- 对于集合类型的扩展在此支持 -->
</property>
<property name="address" value="Cq"></property>
<property name="pat" ref="pat"></property>
</bean>
<!-- 通过cons注入 -->
<bean id="action" class="spring.bean.Action">
<constructor-arg name="type" value="test" index="0" type="String"></constructor-arg>
</bean>
<!--自动装配 byType的话不支持多个Bean-->
<!--bean id="pat2" class="spring.bean.Animals" scope="singleton">
<property name="type" value="cat"></property>
</bean-->
<bean id="person2" class="spring.bean.Person" autowire="byName">
<property name="name" value="bob"></property>
<property name="address" value="Cq2"></property>
</bean>
</beans>
Bean生命周期
Spring提供了一些标志接口,来改变BeanFactory中bean的生命行为,如:InitializingBean、DisposableBean,对应构造、析构逻辑
实现了BeanFactoryAware接口的类,解释为被BeanFactory创建后,拥有一个指向创建它的BeanFactory的引用。通俗讲即BeanFactory通过这个接口来调用Bean,完成如名字等信息的通知。
示例Bean类:
package spring.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
/**
* 通过实现接口干预Bean生命周期,观察效果
* 其实老版本spring提供的init-method和destroy-method方式可以和代码解耦
*/
public class Person implements InitializingBean, DisposableBean, BeanFactoryAware, BeanNameAware {
private String address;
private String name;
private Animals pat;
private BeanFactory factory;
private String beanName;
public Animals getPat() {
return pat;
}
public void setPat(Animals pat) {
this.pat = pat;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 通过<bean>的init-method属性指定的初始化方法
public void myInit() {
System.out.println("【init-method】调用<beanXML>的init-method属性指定的初始化方法");
}
// 通过<bean>的destroy-method属性指定的初始化方法
public void myDestroy() {
System.out.println("【destroy-method】调用<beanXML>的destroy-method属性指定的初始化方法");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Person is Initializing Bean");
}
@Override
public void destroy() throws Exception {
System.out.println("Person is Destroying Bean");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware works here");
this.factory = beanFactory;
}
@Override
public void setBeanName(String name) {
this.beanName = name;
}
}
Spring内部通过BeanPostprofessor来处理能找到的标志接口和调用适当的方法,通俗讲,BeanFactory创建每一个bean,在初始化前后会得到各一个回调函数,BeanFactoryPostProfessor类似,但是通过factory管理时绑定方式不同,关注度低。
package spring.beanlife;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* BeanFactory创建每一个bean,在初始化前后会得到各一个回调函数
*/
public class MyBeanPostProfessor implements BeanPostProcessor {
public MyBeanPostProfessor()
{
System.out.println("【BeanPostProcessor实现类构造器!!】");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
{
System.out.println("BeanPostProcessor接口方法BeforeInitialization对属性进行更改!");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
{
System.out.println("BeanPostProcessor接口方法AfterInitialization对属性进行更改!");
return bean;
}
}
BeanPostProfessor的自定义触发、扩展,与上方实现类等价,一个体系。
package spring.beanlife;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import java.beans.PropertyDescriptor;
/**
* 也是BeanPostProfessor的实现体系,只是扩展了postProcessPropertyValues方法
*/
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
// 实例化Bean之前调用
@Override
public Object postProcessBeforeInstantiation(@SuppressWarnings("rawtypes") Class beanClass,
String beanName) {
System.out.println("InstantiationAwareBeanPostProcessor"
+ "调用BeforeInstantiation方法");
return null;
}
// 实例化Bean之后调用
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("InstantiationAwareBeanPostProcessor"
+ "调用AfterInitialization方法");
return bean;
}
// 设置某个属性时调用
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
PropertyDescriptor[] pds, Object bean, String beanName) {
System.out.println("InstantiationAwareBeanPostProcessor"
+ "调用PropertyValues方法,bean设置属性时触发");
return pvs;
}
}
结果示意:
package spring.beanlife;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import spring.bean.Person;
/**
*生命周期管理
*/
public class BeanLifeTest {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
//MyBeanPostProfessor
MyInstantiationAwareBeanPostProcessor bp = new MyInstantiationAwareBeanPostProcessor();
factory.addBeanPostProcessor(bp);
Person p = (Person) factory.getBean("person");
System.out.println(JSONObject.toJSONString(p));
}
}
可以看到加载顺序:BeanPostProfessor->BeanFactoryAware->afterPropertiesSet->init标签->BeanPostProcessor调用AfterInitialization方法
基于注解实现
spring-2.0及2.5引入了注解模式
@Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里,添加的bean的id为方法名。
扫描bean通过xml配置,context:component-scan有一个use-default-filters属性默认为true,会扫描指定包下的全部的标识类。扩充标签支持了排除bean和扫描bean的方式,context:include-filter、context:include-filter ,filter过滤器有五种type:
assignable-指定扫描某个接口派生出来的类
annotation-指定扫描使用某个注解的类
aspectj-指定扫描AspectJ表达式相匹配的类
custom-指定扫描自定义的实现了org.springframework.core.type.filter.TypeFilter接口的类
regex-指定扫描符合正则表达式的类
示例如下:
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描路径 -->
<context:component-scan base-package="spring.*" use-default-filters="false">
<!-- 扫描包路径下所有注解类标识的bean,并非只有Component -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
</beans>
注解示例代码:
package spring.annotation;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import spring.bean.Action;
import spring.bean.User;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
@Service(value = "UserService")
public class UserService {
@Resource
User user;
@Autowired
@Qualifier("user2")
Action action;
@PostConstruct
private void init()
{
System.out.println("Init with postConstruct");
}
@PreDestroy
private void destroy()
{
System.out.println("destroy with preDestroy");
}
public void getUser()
{
System.out.println(JSONObject.toJSONString(user));
System.out.println(JSONObject.toJSONString(action));
}
public User getBeanUser()
{
return user;
}
}
涉及实体类:
package spring.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class User {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//Value用法
@Value("${is_center:true}:bob")
private String name;
@Bean
public Animals getAnimals()
{
return new Animals();
}
}
package spring.bean;
import org.springframework.stereotype.Component;
@Component(value = "user2")
public class Action {
private String type;
public Action()
{
}
public Action(String type)
{
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
/**
* 通过子类干扰Bean加载
*/
@Component
class ActionSon extends Action
{
}
主程序:
package spring.annotation;
import com.alibaba.fastjson.JSONObject;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring.bean.Animals;
/**
* bean名默认类名首字母小写
*/
public class AnnotationTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
UserService sev = (UserService) context.getBean("UserService");
sev.getUser();
System.out.println(JSONObject.toJSONString(sev.getBeanUser().getAnimals()));
}
}
bean作用域
对于有状态的自然应该定义为prototype,非线程安全的自然可以通过threadlocal或者枷锁保证,全局变量也可以改为局部变量。