Spring

2019-05-30  本文已影响0人  小任务大梦想

什么是Spring框架?

 Spring框架是个轻量级的Java EE框架。所谓轻量级,是指不依赖于容器就能运行的,并且在运行的时候不会消耗大量的资源,内存,CPU等。Struts、Hibernate也是轻量级的。 Spring的核心是IOCAOP,所谓IOC是Inversion of Control 的缩写,它是指控制反转模式(也称作依赖注入), 负责创建对象,管理对象,装配对象,配置对象,并且管理这些对象的整个生命周期。而AOP是Aspect Oriented Programming的缩写,面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

spring框架的优点?

(1)低侵入式设计,代码污染极低
(2)独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺。
(3)Spring的DI机制降低了业务对象替换的复杂性,提高了组件之间的解耦
(4)Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用
(5)Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问
(6)Spring并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部

Spring注解

  - @Repository:标注数据访问层。(dao层)
  - @Service:标注一个业务逻辑层。(service层)
  - @Controller:标注一个控制层。 (controller)
  - @Component :没有明确的分类,当确定不了分类时,可以使用该注解标注。尽量少使用。

  被注解的java类当做Bean实例,Bean实例的名称默认是Bean类的首字母小写,在加上注解之后不要忘记在Spring的配置文件中加上自动扫描哪些包下面的注解哦。

  - @Bean 等同于xml配置中的

<beans>
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
</beans>

  - @Value:为属性注入值。

@Value("Qiqi")
private String name;
//name的值为 Qiqi

  - @Autowired:自动装配,默认是根据类型注入,用于替代基于XML配置的自动装配。可用于为类的属性、构造器、方法进行注值 。
  - @Resource:相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按byName自动注入。它有两个属性,name和type,name属性为bean的名字,type属性为bean的类型。如果使用name属性,则使用byName的自动注入策略,使用type属性时则使用byType自动注入策略。
  - @Qualifier 与 @Autowired配合使用,@Qualifier(“指定加载类的名字”)与@Autowired标注的类进行装配。

 关于切面(AOP)相关注解(具体例子详见下面AOP使用注解配置)
  - @Aspect 标注一个切面类。
  - @PointCut 标注切点 。
  - @After 后置通知,在方法执行之后执行
  - @Before 前置通知,在方法执行之前执行
  - @Around 环绕通知,在方法执行之前与之后执行

  - @Scope 创建的 Bean 对象相对于其他 Bean 对象的请求可见范围,默认是单例模式@Scope(value="Singleton")。 相当于xml文件中的

<bean id="XXX" class="com.XXX.XXXXX" scope="XXXX" />

bean的作用域包括:
 Singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例, 是默认模式。
 Protetype: 每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例。
 Request: web项目中,对于每次HTTP请求,使用request定义的Bean都将产生一个新实例
 Session:web项目中,对于每次HTTP Session,使用session定义的Bean都将产生一个新实例
 GlobalSession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例

  - @Configuration标注当前类为配置类,相当于xml形式的Spring配置
  - @Bean 注解在方法上,标注当前方法的返回值为一个bean。
  - @ComponentScan 会自动扫描指定包路径下面的所有@Controller、@Repository、@Service、@Component 的类,属性有: value指定扫描的包,includeFilters包含那些过滤,excludeFilters不包含那些过滤,相当于xml中的

        <context:component-scan base-package="main.java">
                <context:exclude-filter type="annotation" expression="XX"/>
                <context:include-filter type="annotation" expression="XX"/>
        </context:component-scan>

  - @PostConstruct,标注在方法上,这个方法就会在Bean初始化之后被Spring容器执行。用于访问父类中不可重写的方法或属性。例如当子类想访问父类中标注为final的方法的时候,可以使用该注解,通过super.父类标注为final的方法名就可以访问了。

  - @PreDestroy,标注在方法上,这个方法就会在Bean初始化之后被Spring容器执行。其用法同@PostConstruct。和@PostConstruct 区别在于:@PostConstruct注释的方法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。

IOC

 Spring的核心是IOCAOP,所谓IOC是Inversion of Control 的缩写,它是指控制反转模式(也称作依赖注入,依赖注入是通过Java反射机制实现的,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中), 负责创建对象,管理对象,装配对象,配置对象,并且管理这些对象的整个生命周期。

BeanFactory与ApplacationContext的区别

 IOC中最核心的接口是Beanfactory提供IOC的高级服务,BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。

 ApplicationContext接口:ApplicationContext是建立在BeanFactory基础之上提供抽象的面向应用的服务,它由BeanFactory接口派生而来,因而具有BeanFactory所有的功能。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功能:
• MessageSource, 提供国际化的消息访问
 由于ApplicationContext扩展了MessageResource接口,因而具有消息处理的能力。

    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
            <property name="basenames">
                <list>
                    <value>messageTozh</value><!--国际化文件名称messageTozh_zh_CN.properties,名称格式为XX_zh_CN.properties  -->
                </list>
            </property>
        </bean>


messageTozh_zh_CN.properties文件内容:
    name = \u59d3\u540d{0}
    date = \u73b0\u5728\u65f6\u95f4\u662f:{0}

注意:文件中的中文容易出现乱码,要做适当的处理

测试:

@Test
    public void returnMessage() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        String name = applicationContext.getMessage("name", new String[] {"琪琪"},Locale.getDefault());
        String date = applicationContext.getMessage("date", new Object[]{new Date()},Locale.getDefault());
        
        System.out.println(name + "\n"+date);
    }

• 资源访问,如URL和文件 (Resource接口)
  - ApplicationContext.getResource 默认在工程的一级目录下寻找资源文件。

@Test
    public void Accesspath(){
        ApplicationContext applicationContext1 = new ClassPathXmlApplicationContext("applicationContext.xml");
        Resource resource = applicationContext1.getResource("classpath:db.properties");
        System.out.println("文件是否存在:"+resource.exists());
    }

• 事件机制
 ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。
   ApplicationEvent:容器事件,必须由ApplicationContext发布
   ApplicationListener:监听器,可由容器中的任何监听器Bean担任

   (1) . Java类继承了ApplicationEvent类,那么该对象即可以作为Spring容器的容器事件。
   (2) . 容器事件的监听器类必须实现ApplicationListener接口,实现该接口实现了
onApplicationEvent(ApplicationEvent event)方法:表示每当容器内发生任何事件时,此方法都会被触发。
  (3) . Spring中配置一个实现了ApplicationListener的Bean,Spring容器就会把这个Bean当成容器事件的监听器。
  (4) . 调用ApplicationContext的publishEvent()方法来主动触发一个容器事件。

 以下一个年龄监控例子来说明Spring的事件机制:

//此处省略Student类

package login.event;

import org.springframework.context.ApplicationEvent;


/***
 * 容器事件
 * @author wq
 *
 */
public class AgeEvent extends ApplicationEvent {

    private Student student;
    public AgeEvent(Student student) {
        super(student);
        this.student = student;
    }
    public Student getStudent() {
        return student;
    }
    public void setStudent(Student student) {
        this.student = student;
    }
    
}


package login.event;


import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;


/***
 * 容器事件监听器
 * @author wq
 *
 */
public class AgeEventListener implements ApplicationListener{

    @Override
    public void onApplicationEvent(ApplicationEvent evt) {
        if(evt instanceof AgeEvent){
            
            AgeEvent ageEvent = (AgeEvent) evt;
            
            Student student = ageEvent.getStudent();
            
            if(student.getAge() > 16 && student.getAge() < 19 ) {
                System.out.println(student.getName()+","+student.getAge()+"这个年龄普遍是高中年龄范围");
            }
            if(student.getAge() <= 22 && student.getAge() >=18){
                System.out.println(student.getName()+","+student.getAge()+"这个年龄普遍是大学毕业");
            }else if(student.getAge()>23){
                System.out.println(student.getName()+","+student.getAge()+"这个年龄超出监控范围");
            }
            
        }
        
    }

}

//Spring xml文件配置
<!--事件机制  -->
        <bean id="student" class="login.event.Student">
            <property name="name">
                <value>琪琪</value>
            </property>
            <property name="age">
                <value>27</value>
            </property>
        </bean>
<!--配置监听器  -->
<bean id="ageEventListener" class="login.event.AgeEventListener"></bean>

//测试
@Test
    public void rerturnAgeListener() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student =  (Student) applicationContext.getBean("student");
        applicationContext.publishEvent(new AgeEvent(student));
    }
    

Spring还提供了几个内置事件:
  - ContextRefreshedEvent:ApplicationContext容器初始化或刷新时触发该事件。
  - ContextStartedEvent:当使用ConfigurableApplicationContext(ApplicationContext的子接口)接口的start()方法启动ApplicationContext容器时触发该事件。需要停止后重新启动的场合比较常见。
  - ContextClosedEvent:当使用ConfigurableApplicationContext接口的close()方法关闭ApplicationContext时触发该事件。
  - ContextStoppedEvent:当使用ConfigurableApplicationContext接口的stop()方法使ApplicationContext容器停止时触发该事件。
  - RequestHandledEvent:Web相关事件,只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。

• Spring上下文获取
 Spring上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。

 通过ApplicationContextAware加载Spring上下文环境,通过它Spring容器会自动把上下文环境对象调用ApplicationContextAware接口中的setApplicationContext方法。

package util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component //如果没有使用注解的方式,使用xml的方式,记得在spring的xml中加入<bean id="springContextUtil" class="XX"  />
public class SpringContextUtil implements ApplicationContextAware{

     private static ApplicationContext applicationContext;//spring上下文
    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
         SpringContextUtil.applicationContext=applicationContext;
    }
    public static ApplicationContext getApplicationContext(){
        return applicationContext;
    }
 
    public static <T> T getBean(String name) throws BeansException{
           return (T)applicationContext.getBean(name);
    }
}
//配置web.xml文件
<!--初始化Spring容器,让Spring容器随Web应用的启动而自动启动  -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

 然后我们可以通过该方法的getBean()方法得到Spring容器中的对象了。

 我们还可以通过 new ClassPathXmlApplicationContext("applicationContext.xml");这种方式来获取Spring上下文环境,但是这种方法通常是用在测试环境中的,在系统不启动的情况下手动初始化Spring上下文再获取对象。在web项目中,系统一旦启动,web服务器会初始化Spring的上下文的,如果使用该方法,所有bean会再被创建一次,会造成重复,系统加载速度慢。

依赖注入的三种方式

  - setter注入(主要)
  - 接口注入
  - 构造方法注入(主要)

Spring管理bean

 程序主要是通过Spring容器来访问容器中的Bean,ApplicationContext是Spring容器最常用的接口,该接口有如下两个实现类:
ClassPathXmlApplicationContext: 从类加载路径下搜索配置文件,并根据配置文件来创建Spring容器。
FileSystemXmlApplicationContext: 从文件系统的相对路径或绝对路径下去搜索配置文件,并根据配置文件来创建Spring容器。

public void returnResult() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        LoginManager loginManager = (LoginManager) applicationContext.getBean("loginManager");
        loginManager.add();
    }
spring创建bean的3种方式

  - 构造器创建bean实例,当没有采用构造方式注入bean的时候,spring会去调用无参构造方法来创建bean,因此必须要提供无参构造器。

 <!-- 不带参数的构造器 --> 
 <bean id="login" class="com.flowers.login.beans.LoginBean" > </bean> 

 <bean id="login" class="com.flowers.login.beans.LoginBean" >  
      <!-- 创建一个value为琪琪的name -->  
     <constructor-arg name="name" value="琪琪"></constructor-arg>  
 </bean> 

  <!-- 有参构造器 -->  
 <bean id="login" class="com.flowers.login.beans.LoginBean">
        <!-- index表示参数顺序,type为参数类型  value为参数值 -->
        <constructor-arg value="琪琪" index="0" type="java.lang.String"></constructor-arg>
        <!-- 直接赋值 -->
        <constructor-arg value="琪琪" index="1" type="java.lang.String"></constructor-arg> 
        <!-- ref:通过引用方式赋值 -->
        <constructor-arg index = "1" type = "java.lang.String" ref="login"></constructor-arg>
 </bean>

  - 使用静态工厂方法创建bean实例,class属性必须指定,Spring通过该属性知道由哪个工厂类来创建Bean实例。采用静态工厂方法创建bean时,<bean />元素需要制定两个属性:
    class: 该属性的值为静态工厂类的类名。
    factory-method: 指定静态工厂的方法来生产bean实例。

 <bean id="login" class="com.flowers.login.LoginFactory" factory-method="getStaticFactory"></bean>

  - 使用实例工厂方法创建bean实例,配置Bean实例的<bean.../>元素无须class属性,配置实例工厂方法使用factory-bean指定工厂实例。采用实例工厂方法创建Bean的<bean.../>元素时需要指定如下两个属性:
    factory-bean: 该属性的值为工厂Bean的id。
    factory-method: 指定实例工厂的工厂方法。

    <bean id="factory" class="com.flowers.login.LoginFactory"></bean>
    <bean id="user4" factory-bean="factory" factory-method="getfactory"></bean>
Spring Bean的生命周期

 (1) . 实例化一个bean,就是我们通常干的new
 (2) . 根据spring上下文对实例化的bean进行配置,通常就是指IOC注入。
 (3) . 如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值;
 (4) . 如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身;
 (5) . 如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;
 (6) . 如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
 (7) . 如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
 (8) . 如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;
 (9) . 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用DisposableBean的destroy()方法;
 (10) . 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

Spring中bean的加载过程

(1)获取配置文件资源;
(2)对获取的xml资源进行一定的处理检验;
(3)处理包装资源;
(4)解析处理包装过后的资源;
(5)加载提取bean并注册(添加到beanDefinitionMap中)。

在Spring框架中共有5种自动装配:

<bean id="login" class="com.flowers.login.beans.LoginBean" autowire="XX"> </bean>

autowire的值有:
(1)no:在该设置下自动装配是关闭的,开发者需要自行在bean定义中用标签明确的设置依赖关系。
(2)byName:根据setter方法名进行自动装配。Spring容器查找容器中全部Bean,找出其id与setter方法名去掉set前缀,并小写首字母后同名的Bean来完成注入。如果找到的话,就装配这个属性,如果没找到的话就报错。
(3)byType:该选项可以根据bean类型设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的类型自动在在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。
(4)constructor:构造器的自动装配和byType模式类似,但是仅仅适用于与有构造器相同参数的bean,如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
(5)default:表示默认采用上一级标签的自动装配的取值。如果存在多个配置文件的话,那么每一个配置文件的自动装配方式都是独立的。

注:有多个类型的bean则无法完成自动装配

 <!--注入一般属性,spring注入方式一般有属性注入,构造方法注入,set注入  -->
    <bean id="loginDaoImpl" class="com.flowers.login.dao.LoginDaoImpl"></bean>
    <bean id="loginManager" class="com.flowers.login.impl.LoginManager">
        <property name="loginDao" ref="loginDaoImpl"></property>
    </bean>

 当一个Bean既使用自动装配依赖,又使用ref显式指定依赖时,则显式指定的依赖覆盖自动装配依赖;对于大型的应用,不鼓励使用自动装配。虽然使用自动装配可减少配置文件的工作量,但大大将死了依赖关系的清晰性和透明性。依赖关系的装配依赖于源文件的属性名和属性类型,导致Bean与Bean之间的耦合降低到代码层次,不利于高层次解耦。

AOP

 AOP是Aspect Oriented Programming的缩写,面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP的基本概念

 AOP常用有切点(Pointcut)、连接点(Join point)、切面(Aspect)、引入(Introduction)、织入(Weaving)、通知(Advice)等。
 - 切点(Pointcut):选择一组相关连接点的模式,即可以认为连接点的集合,在AOP中表示为“在哪里干的集合”;

 - 连接点(Join point):程序执行过程中明确的点,如连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等,在springMVC中,连接点总是方法的抛出。 Spring只支持方法执行连接点,在AOP中表示为“在哪里干”;

 - 切面(Aspect):切面用于组织多个Advice,Advice放在切面定义中。在Spring中可以使用Schema和@AspectJ方式进行组织实现;在AOP中表示为“在哪干和干什么集合”;切面将那些与业务无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。

 - 引入(Introduction):也称为内部类型声明,将方法或字段添加到被处理的类中。Spring允许引入新的接口(必须对应一个实现)到任何被处理的对象(目标对象)中, 在AOP中表示为“干什么(引入什么)”;

 - 织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期、类装载期、运行期进行。在运行时完成织入。

 - 通知(Advice):在连接点上执行的行为,通知提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段;包括前置通知(before advice)、后置通知(after advice)、环绕通知(around advice),在Spring中通过代理模式实现AOP,并通过拦截器模式以环绕连接点的拦截器链织入通知;在AOP中表示为“干什么”;

对Spring AOP的配置

 对于Spring AOP的配置有两种方法,一种是使用注解,另一种是xml文件配置。

  - 使用注解配置

package test;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class MyAspectForAnnotation {

     /**
     * 前置通知
     */
    @Before("execution(public * login.manager.LoginManager.add())")
    public void before(){
        System.out.println("注解前置通知....");
    }

    /**
     * 成功返回后的通知
     * 
     */
    @AfterReturning(value="execution(public * login.manager.LoginManager.add())",returning="returnVal")
    public void afterReturning(Object returnVal){
        System.out.println("注解成功返回后的通知...."+returnVal);
    }


    /**
     * 环绕通知
     * @param joinPoint 可用于执行切点的类
     * @return
     * @throws Throwable
     */
    @Around("execution(public * login.manager.LoginManager.add())")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("注解环绕通知前....");
        Object obj= (Object) joinPoint.proceed();
        System.out.println("注解环绕通知后....");
        return obj;
    }

    /**
     * 抛出通知
     * @param e
     */
    @AfterThrowing(value="execution(public * login.manager.LoginManager.add())",throwing="e")
    public void afterThrowable(Throwable e){
        System.out.println("注解出现异常:msg="+e.getMessage());
    }

    /**
     * 后置通知,不管被通知的方法是否执行成功
     */
    @After("execution(public * login.manager.LoginManager.add())")
    public void after(){
        System.out.println("注解后置通知,不管被通知的方法是否被执行成功....");
    }
    
    
}

xml配置:
 <aop:aspectj-autoproxy />  <!--使用AOP注解  -->
         <!--定义切面类  -->
        <bean id="myAspect" class="test.MyAspectForAnnotation"></bean>
        
        <bean id="loginDaoImpl" class="login.dao.LoginDaoImpl"></bean>
        <bean id="loginManager" class="login.manager.LoginManager">
            <property name="loginDao" ref="loginDaoImpl"></property>
        </bean>

测试类:
@Test
    public void returnResultForAnnotation() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("AOPforAnnotation.xml");
        ILoginManager iloginManager = (ILoginManager) applicationContext.getBean("loginManager");
        iloginManager.add();
    }


  - 使用xml文件配置

           <bean id="myAspect" class="AOP方法路径"></bean>
            <aop:config>
              <aop:pointcut expression="execution(方法类型 * 方法路径.方法名()")" id="servicePointcut"/> // 配置切点
                  <aop:aspect id="logAspect" ref="myAspect"> //配置切面
                      <aop:before method="before"  pointcut-ref="servicePointcut" /> //通知
                  </aop:aspect>
            </aop:config>       

例如:

package test;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspectForXml {
     /**
     * 前置通知
     */
    public void before(){
        System.out.println("前置通知....");
    }

    /**
     * 成功返回后的通知
     * 
     */
    public void afterReturning(Object returnVal){
        System.out.println("成功返回后的通知...."+returnVal);
    }


    /**
     * 环绕通知
     * @param joinPoint 可用于执行切点的类
     * @return
     * @throws Throwable
     */
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前....");
        Object obj= (Object) joinPoint.proceed();
        System.out.println("环绕通知后....");
        return obj;
    }

    /**
     * 抛出通知
     * @param e
     */
    public void afterThrowable(Throwable e){
        System.out.println("出现异常:msg="+e.getMessage());
    }

    /**
     * 后置通知,不管被通知的方法是否执行成功
     */
    public void after(){
        System.out.println("后置通知,不管被通知的方法是否被执行成功....");
    }
}


xml文件配置:
        <bean id="loginDaoImpl" class="login.dao.LoginDaoImpl"></bean>
        <bean id="loginManager" class="login.manager.LoginManager">
            <property name="loginDao" ref="loginDaoImpl"></property>
        </bean>
        
        <!--AOP xml配置  -->
        <!--定义切面  -->
        <bean id="myAspect" class="test.MyAspectForXml"></bean>
        <!--配置切面  -->
            <aop:config>
            <!--定义切点函数  -->
            <aop:pointcut expression="execution(public * login.manager.LoginManager.add())" id="addPointcut"/>
            
            <aop:aspect ref="myAspect" order="0">
                <!--前置通知  -->
                <aop:before method="before"  pointcut-ref="addPointcut" />
                <!--成功返回后的通知  -->
                <aop:after-returning method="afterReturning" pointcut-ref="addPointcut" returning="returnVal"/>
                <!--环绕通知  -->
                <aop:around method="around" pointcut-ref="addPointcut"/>
                <!--抛出通知  -->
                <aop:after-throwing method="afterThrowable" pointcut-ref="addPointcut" throwing="e"/>
                <!--后置通知,不管被通知的方法是否被执行成功  -->
                <aop:after method="after"  pointcut-ref="addPointcut" />
                
            </aop:aspect>
        </aop:config>
注意:after-returning和after-throwing需要加入参数。returning=“XX”和throwing=“XX”,order越小越是最先执行,最先执行的最后结束。

测试类:
package test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import login.manager.ILoginManager;


public class LoginTest {
    @Test
    public void returnResult() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        ILoginManager iloginManager = (ILoginManager) applicationContext.getBean("loginManager");
        iloginManager.add();
        
    }

}


AOP的动态代理

 1、jdk动态代理
 2、cglib动态代理
Spring在选择用JDK动态代理还是CGLiB动态代理的依据:
  (1)当Bean实现接口时,Spring就会用JDK的动态代理
  (2)当Bean没有实现接口时,Spring使用CGlib是实现
 如果是单例,则可以选择CGlib动态代理,如果是多列则选择JDK动态代理,因为JDK在创建代理对象时的性能要高于CGLib代理,而生成代理对象的运行性能却比CGLib的低。

上一篇下一篇

猜你喜欢

热点阅读