Spring基于xml配置与注解驱动开发(二)

生命周期

基于xml配置

init-method和init-destroy

spring-context.xml

<bean id="car" class="com.yt.model.Car" init-method="init" destroy-method="destroy"/>

Car类

public class Car {
    public Car() {
        System.out.println("car Constructor");
    }
    public void init() {
        System.out.println("car ... init");
    }
    public void destroy() {
        System.out.println("car ... destroy");
    }
}

在调用构造器方法后,调用初始化方法init(),当容器关闭时调用销毁方法destroy()。

InitializingBean和DisposableBean

public class Cat implements InitializingBean,DisposableBean {
    public Cat() {
        System.out.println("cat constructor");
    }
    /**
     * 在初始化属性赋值之后调用该方法
     */
    public void afterPropertiesSet() throws Exception {
        System.out.println("cat ... afterPropertiesSet");
    }
    public void destroy() throws Exception {
        System.out.println("cat destroy");
    }
}

该方式实现的生命周期与spring容器的接口耦合,不推荐使用。

BeanPostProcessor

实现BeanPostProcessor接口并注册到IOC容器中,IOC会在初始化每一个bean前后调用该接口相应的方法,当有多个BeanPostProcessor实现时,可以用@Order指定加载顺序。

@Component
@Order(value = 1)
public class MyBeanPostProcessor implements BeanPostProcessor{
    /**
     * @param bean
     * @param beanName
     * @return 返回原来的bean或包装过的bean
     * @throws BeansException
     */
    //初始化前调用,在afterPropertiesSet和init-method方法前
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(bean +"-----"+ beanName+"---beforeInit");
        return bean;
    }
    //初始化后调用,在afterPropertiesSet和init-method方法之后
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(bean + "-----" + beanName+"---afterInit");
        return bean;
    }
}

基于注解配置

init-method和init-destroy

@Configuration
public class MainConfigLifeCycle {
     /**
     * 初始化:对象创建完成,并赋值后,调用初始化方法
     * 销毁:
     *       singleton:容器关闭时调用
     *       prototype:容器不会管理这个bean,容器不会调用销毁方法
     * @return
     */
    @Bean(initMethod = "init",destroyMethod = "destroy")
    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
    public Car car() {
        return  new Car();
    }
}

@PostConstruct和@PreDestroy

public class Car {
    public Car() {
        System.out.println("car Constructor");
    }
    @PostConstruct
    public void init() {
        System.out.println("car ... init");
    }
    @PreDestroy
    public void destroy() {
        System.out.println("car ... destroy");
    }
}

组合生命周期机制

为同一个bean配置多个生命周期机制,不同的初始化方法,调用如下:

  • @PostConstruct 元注释
  • InitializingBean 的 afterPropertiesSet() 定义
  • 自定义init() 方法

析构方法调用顺序是相同的:

  • @PreDestroy 元注释
  • DisposableBean 的 destroy() 定义
  • 自定义 destroy() 方法

Properties引入与赋值

基于xml配置

properties

<context:property-placeholder location="classpath:jdbc.properties" order="1"/>
<context:property-placeholder location="classpath:variable.properties" order="2"/>
<bean id="dataSourece" class="com.mchange.v2.c3p0.ComboPooledDataSource">
     <property name="driverClass" value="${driverClass}"/>
     <property name="jdbcUrl" value="${url}"/>
     <property name="user" value="${userName}"/>
     <property name="password" value="${password}"/>
 </bean>

基于注解配置

@PropertySources与@PropertySource

@PropertySources({
    @PropertySource(value = {"classpath:variable.properties"}),
    @PropertySource(value = "classpath:jdbc.properties")
})
@Configuration
@ComponentScan(value = {"com.yt.conf.value"})
public class MainConfigValue {
}

@PropertySources中可定义多个@PropertySource,也可以单独定义多个@PropertySource

@Value

@Component
public class Pig {
    @Value("张三")
    private String name;
    @Value("#{20-1}")       //SpEl表达式
    private int age;
    @Value("${userName}")   //properties属性文件引入
    private String phone;
}

@value可以给属性、参数、方法赋值,值可以是字符串、SpEL和EL表达式。

自动装配

Spring规范的注解

@Autowired

//注解在字段上
@Autowired(required = true)
private Custom custom;
//注解在有参构造器上
@Autowired
public CustomDao(Custom custom) {
    this.custom = custom;
}
//注解在set方法上
@Autowired
public void setCustom(Custom custom) {
     this.custom = custom;
 }

@Autowired:优先根据类型去容器中查找,如果有多个则根据属性名去查找,required 默认为true,当required=false时可以找不到装配的实例。

@Qualifier

@Qualifier指定需要装配的组件的id

 @Autowired(required = true)
 @Qualifier("customService1")
 private CustomService customService;

@Primary

当有多个同类型的bean被自动装配时,注解了@Primary的bean会被优先装配。

java规范的注解

@Resource(JSR250)

@Resource与@Autowired注解类似,都可以实现自动装配,区别在于@Resource是默认是根据name作为id装配,并且不可结合@Quaifier、@Primary使用

@Inject(JSR330)

和Autowired功能一样,区别在于缺少Autowired的required属性

@Profile(环境配置)

使用@profile注解的bean只有在被激活时才会被加载,默认为”default”,可以利用此注解控制测试、开发和生产三种不同环境中bean的加载。
-VM options:-Dspring.profiles.active=test

@Configuration
@PropertySource(value="classpath:jdbc.properties")
public class ProfileConfig implements EmbeddedValueResolverAware{
    StringValueResolver resolver;
    private String driverClass;
    @Value("${userName}")
    private String user;

    @Bean
    @Profile(value = "test")
    public DataSource testDataSource(@Value("${password}")String pwd) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setJdbcUrl("${url}");
        dataSource.setDriverClass(driverClass);
        dataSource.setPassword(pwd);
        System.out.println("test"+pwd);
        return dataSource;
    }
    @Bean
    @Profile(value = "dev")
    public DataSource devDataSource(@Value("${password}")String pwd) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setJdbcUrl("${url}");
        dataSource.setDriverClass(driverClass);
        dataSource.setPassword(pwd);
        System.out.println("dev"+pwd);
        return dataSource;
    }
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.resolver = resolver;
        driverClass = resolver.resolveStringValue("${driverClass}");
    }
}

Aware接口

容器管理的Bean一般不需要了解容器的状态和直接使用容器,但是在某些情况下,是需要在Bean中直接对IOC容器进行操作的,这时候,就需要在Bean中设定对容器的感知,Spring IOC容器也提供了该功能,它是通过特定的Aware接口来完成的。

Aware 作用
BeanNameAware 可以在Bean中得到它在IOC容器中的Bean的实例的名字
BeanFactoryAware 可以在Bean中得到Bean所在的IOC容器BeanFactory,从而直接在Bean中使用IOC容器的服务
ApplicationContextAware 可以在Bean中得到Bean所在的应用上下文ApplicationContext,从而直接在Bean中使用上下文的服务
MessageSourceAware 在Bean中可以得到消息源
ApplicationEventPublisherAware 在bean中可以得到应用上下文的事件发布器,从而可以在Bean中发布应用上下文的事件
ResourceLoaderAware 在Bean中可以得到ResourceLoader,从而在bean中使用ResourceLoader加载外部对应的Resource资源
EnvironmentAware 在Bean中得到Environment,从而在Bean中设置上下文环境
EmbeddedValueResolverAware 在Bean中得到StringValueResolver,从而在Bean中解析EL或SpEL表达式值
ImportAware 在Bean中得到AnnotationMetadata
ServletConfigAware 在Bean中得到ServletConfig
LoadTimeWeaverAware 在Bean中得到LoadTimeWeaver
BeanClassLoaderAware 在Bean中得到ClassLoader
ServletContextAware 在Bean中得到ServletContext

每个Aware接口都有对应的BeanPostProcessor实现。实现Aware接口的每个bean被初始化前后都会有相应的BeanPostProcessor被匹配,这些BeanPostProcessor会调用该bean实现了Aware接口的方法,这些方法都是set方法。示例如@Profile中实现了EmbeddedValueResolverAware接口的类可以通过setEmbeddedValueResolver方法获取StringValueResolver实例。

AOP

Spring代理

JDK的代理是必须要实现接口的,CGLIB代理是生成的目标类的子类,所以类和方法不能声明为final的,要不然就会有问题,spring中默认使用jdk 的动态代理(实现接口了),除强制设置CGLIB的代理

JDK代理(只能代理有父接口的类)

Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object proxy,Method method, Object[] args)。在实际使用时,第一个参数proxy一般是指代理类,method是被代理的方法,args为该方法的参数数组。
Proxy : 该类即为动态代理类

接口和被代理类

public interface Arithmetic {
   public int add(int a,int b);
   public int division(int a,int b);
}
@Repository
public class ArithmeticImpl implements Arithmetic{
    public int add(int a,int b){
        System.out.println(a+b);
        return a+b;
    }
    public int division(int a,int b){
        System.out.println(a/b);
        return a/b;
    }
}

JDK代理工厂

public class ProxyFactory {

    private Object target;

    public ProxyFactory(Object target) {
        super();
        this.target = target;
    }

    //给目标对象生成代理对象
    public  Object getProxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), 
                new InvocationHandler() {
                    //回调方法  
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        String methodName = method.getName();
                        System.out.println("method="+methodName);
                        Object returnValue = method.invoke(target, args);
                        System.out.println("result="+returnValue);
                        return returnValue;
                    }
                });
        }
}

测试

public class ProxyTest {
    public static void main(String[] args) {
        Arithmetic arithmetic = new ArithmeticImpl();
        //获取代理类
        ProxyFactory proxyFactory = new ProxyFactory(arithmetic);
        Arithmetic proxy = (Arithmetic )proxyFactory.getProxyInstance();
        int resutl = proxy.add(1, 2);
    }
}

Cglib代理

CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。 当然这些实际的功能是asm所提供的,asm又是什么?Java字节码操控框架,ASM是一套java字节码生成架构,它可以动态生成二进制格式的stub类或其它代理类,或者在类被java虚拟机装入内存之前,动态修改类。实际上CGlib为spring aop提供了底层的一种实现;为hibernate使用cglib动态生成VO/PO (接口层对象)

public class CglibProxyFactory implements MethodInterceptor{
    private Object target;

    public CglibProxyFactory(Object target) {
        super();
        this.target = target;
    }
    public Object getProxyInstance(){
        //工具类
        Enhancer en = new   Enhancer();
        //设置父类
        en.setSuperclass(target.getClass());
        //设置回调函数
        en.setCallback(this);
        //创建子类(代理对象)
        return en.create();
    }
    //回调方法
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        String methodName = method.getName();
              System.out.println("method="+methodName);
        Object returnValue = method.invoke(target, args);
        System.out.println("result="+returnValue);
        return returnValue;
    }
}

基于xml配置

    <!--开启AspectJ-->
    <aop:aspectj-autoproxy/>
    <!--配置切面-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.yt.aop.*.*(int,int))"/>
        <aop:aspect ref="arithmeticAspect" order="1">
            <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
            <aop:after method="afterMethod" pointcut-ref="pointcut"/>
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/>
        </aop:aspect>
    </aop:config>

基于注解的配置

开启AOP

@EnableAspectJAutoProxy
public class MainConfig {
}

配置切面类

@Aspect
@Order(value = 2)
public class ArithmeticAspect {
    /**
     * 定义一个方法,用于声明切入点表达式,一般该方法中不需要添加其他代码
     */
    @Pointcut("execution(* com.yt.aop.*.*(int,int))")
    public void declareJoinPointExpression(){}
    /**
     * 前置通知:方法执行之前执行
     * @param joinPoint
     */
    @Before("declareJoinPointExpression()")
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("the method:"+methodName+"begins:"+args);
    }

    /**
     * 后置通知:方法执行后执行,无论方法是否抛出异常,因为不确定方法是否执行正常,所以无法访问到方法的返回值
     * @param joinPoint
     */
    @After("execution(* com.yt.aop.*.*(int,int))")
    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("the method:"+methodName+"end:"+args);
    }

    /**
     * 返回通知:在方法正常结束时执行的代码,返回通知是可以访问到方法的返回值
     * @param joinPoint
     * @param result
     */
    @AfterReturning(value="execution(* com.yt.aop.*.*(int,int))",returning ="result" )
    public void afterReturning(JoinPoint joinPoint,Object result) {
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("the method:"+methodName+"returning:"+args+"return:"+result);
    }

    /**
     * 异常通知:当抛出异常时执行,可以访问到异常对象
     * @param joinPoint
     * @param ex
     */
    @AfterThrowing( value = "execution(* com.yt.aop.*.*(int,int))",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Exception ex){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("the method:"+methodName+"returning:"+args+"exception"+ex);
    }

    /**
     * 环绕通知:必须持有ProceedingJoinPoint参数且具有返回值,类似于动态代理的全过程,ProceedingJoinPoint
     * 可以决定是否执行目标方法
     * @param proceedingJoinPoint(required)
     */
    @Around( value = "execution(* com.yt.aop.*.*(int,int))")
    public Object arroundMethod(ProceedingJoinPoint proceedingJoinPoint){
        Object result = null;
        String methodName = proceedingJoinPoint.getSignature().getName();
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }
}

AOP源码浅析

@EnableAspectJAutoProxy开启AOP功能,首先从@EnableAspectJAutoProxy看起:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;
}

由上可以看出@EnableAspectJAutoProxy 导入了组件AspectJAutoProxyRegistrar,而查看源代码可知AspectJAutoProxyRegistrar是ImportBeanDefinitionRegistrar的实现类,ImportBeanDefinitionRegistrar通过BeanDefinitionRegistry注册组件,关于ImportBeanDefinitionRegistrar的示例详见 Spring基于xml配置与注解驱动开发(一)
BeanDefinitionRegistry注册了AnnotationAwareAspectJAutoProxyCreator

RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);

AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator

由以上继承树发现AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor、Aware和Ordered接口。

未完待续。。。。。。。。。。

猜你喜欢

转载自blog.csdn.net/yutao_struggle/article/details/79768896
今日推荐