Spring

作者: 小任务大梦想 | 来源:发表于2019-05-30 15:13 被阅读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的低。

    相关文章

      网友评论

          本文标题:Spring

          本文链接:https://www.haomeiwen.com/subject/pdmzyftx.html