• spring框架总结


    1. Spring是什么?及其特点。

      Spring框架是一个开源的轻量级容器框架。主要有三大特点:

      (1) 容器。Spring框架是一个容器,能够管理项目中的所有对象。

      (2) IOC(控制反转)。Spring将创建对象的方式反转了,从程序员自己创建反转给了程序。

        DI(依赖注入) 需要有IOC环境,在Spring创建Bean对象时,动态的将依赖对象注入到Bean对象中去。依赖注入和控制反转最大的好处就是解耦合

      (3) AOP(面向切面)。面向切面编程,简而言之,就是将纵向重复的代码横向抽取出来,主要体现在为容器中管理的对象生成动态代理对象。

    2. applicationContext & BeanFactory区

      BeanFactory

        BeanFactory是一个接口,用实例化、配置和管理Bean对象里面有一个getBean()方法获取Bean对象。

           使用BeanFactory(拿到Bean对象) 三种方法:

            方式一:根据id拿,可以确定

            方式二:也是根据idClass实例,可以确定(不需要强转)

            方式三:根据类型,现在有两个类型相等,无法确定(报错)

    //第一步:拿到资源配置文件
    		Resource resource = new ClassPathResource("applicationContext.xml");
    		//第二步:拿到核心对象 BeanFactory
    		BeanFactory factory = new XmlBeanFactory(resource);
    		
    		//通过BeanFactory获取Bean对象的三种方式:
    			//第一种:通过xml文件中配置的id获取对象
    			Object bean1 = factory.getBean("ud");
    			((UserDaoImpl)bean1).add();
    
    			//第二种:通过xml文件中配置的id及类的字节码对象 获取对象
    			UserDaoImpl bean2 = factory.getBean("ud",UserDaoImpl.class);
    			bean2.add();
    
    			//第三种:通过类的字节码对象 获取对象.
    			UserDaoImpl bean3 = factory.getBean(UserDaoImpl.class);
    			bean3.add();	//依赖注入(DI)/控制反转(IOC)
    			
    			//三种方式拿到的Bena对象在内存中的地址都相同
    			System.out.println(bean1==bean2 && bean2==bean3);//true

        以上三种方式是spring框架底层拿对象的方式,我们不会去用这些方式去直接获取bean对象
        获取spring核心对象的首选方式:
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过该对象的getBean()获取具体的bean对象
        UserDaoImpl bean4 = ac.getBean("ud",UserDaoImpl.class);
        UserDaoImpl bean5 = ac.getBean("ud",UserDaoImpl.class);
        System.out.println(bean4 == bean5);   //true

    区别:

         1.  ApplicationContextBeanFactory的子类,拥有更多的功能与方法。比如:提供了文本信息解析工具,包括I18N(国际化的支持;提供了载入文件资源的通用方法;提供了发送事件的功能。

        2.   ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。

          而BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。

        3. BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

      扩展:我们在使用ApplicationContext的时候,可以通过配置让它也变成与BeanFactory一样的懒加载:

        配置一:让所有Bean都变成懒加载

        <beans xmlns="http://www.springframework.org/schema/beans"

        ....

        default-lazy-init="true">

          <bean id="myBean" class="cn.itsource._01_hello.MyBean"></bean>

        </beans>   

        配置二:让其中一个Bean变成懒加载

          <bean id="myBean" class="cn.itsource._01_hello.MyBean" lazy-init="true"></bean>

     

     3. 请解释Spring Bean的生命周期?

    先简单了解:

    生命周期属性(了解)———初始化和销毁
        (1)配置一个方法作为生命周期初始化方法,spring会在对象创建之后立刻调用 init-method
        (2)配置一个方法作为生命周期的销毁方法,spring容器在关闭并销毁所有容器中的对象之前调用destory-method
        <bean init-method=“init”  destory-method=“destory”></bean>        对应注解为@PostConstruct

        <bean name=“hello” class=“完整类名”></bean>                                 对应注解为@PreDestory

    Spring中Bean的管理是其最基本的功能,根据下面的图来了解Spring中Bean的生命周期:

    解说:

    (1)BeanFactoryPostProcessor的postProcessorBeanFactory()方法:若某个IoC容器内添加了实现了BeanFactoryPostProcessor接口的实现类Bean,那么在该容器中实例化任何其他Bean之前可以回调该Bean中的postPrcessorBeanFactory()方法来对Bean的配置元数据进行更改,比如从XML配置文件中获取到的配置信息。

    (2)Bean的实例化:对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。

    (3)Bean属性注入:实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。

    (4)BeanNameAware的setBeanName()方法:如果某个Bean实现了BeanNameAware接口,那么Spring将会将Bean实例的ID传递给setBeanName()方法,在Bean类中新增一个beanName字段,并实现setBeanName()方法。

    (5)BeanFactoryAware的setBeanFactory()方法:如果某个Bean实现了BeanFactoryAware接口,那么Spring将会将创建Bean的BeanFactory传递给setBeanFactory()方法,在Bean类中新增了一个beanFactory字段用来保存BeanFactory的值,并实现setBeanFactory()方法。

    (6)ApplicationContextAware的setApplicationContext()方法:如果某个Bean实现了ApplicationContextAware接口,那么Spring将会将该Bean所在的上下文环境ApplicationContext传递给setApplicationContext()方法,在Bean类中新增一个ApplicationContext字段用来保存ApplicationContext的值,并实现setApplicationContext()方法。

    (7)BeanPostProcessor预初始化方法:如果某个IoC容器中增加的实现BeanPostProcessor接口的实现类Bean,那么在该容器中实例化Bean之后,执行初始化之前会调用BeanPostProcessor中的postProcessBeforeInitialization()方法执行预初始化处理。

    (8)InitializingBean的afterPropertiesSet()方法:如果Bean实现了InitializingBean接口,那么Bean在实例化完成后将会执行接口中的afterPropertiesSet()方法来进行初始化。

    (9)自定义的inti-method指定的方法:如果配置文件中使用init-method属性指定了初始化方法,那么Bean在实例化完成后将会调用该属性指定的初始化方法进行Bean的初始化。

    (10)BeanPostProcessor初始化后方法:如果某个IoC容器中增加的实现BeanPostProcessor接口的实现类Bean,那么在该容器中实例化Bean之后并且完成初始化调用后执行该接口中的postProcessorAfterInitialization()方法进行初始化后处理。

    (11)使用Bean:此时有关Bean的所有准备工作均已完成,Bean可以被程序使用了,它们将会一直驻留在应用上下文中,直到该上下文环境被销毁。

    (12)DisposableBean的destory()方法:如果Bean实现了DisposableBean接口,Spring将会在Bean实例销毁之前调用该接口的destory()方法,来完成一些销毁之前的处理工作。

    (13)自定义的destory-method指定的方法:如果在配置文件中使用destory-method指定了销毁方法,那么在Bean实例销毁之前会调用该指定的方法完成一些销毁之前的处理工作。

     4. 解释Spring支持的几种bean的作用域。

    Spring容器中的bean可以分为5个范围:   scope属性

    (1)singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。

    (2)prototype:多例原型:  被标识为多例的对象,每次在获得才会被创建,每次创建都是新的对象.

    (3)request:Web环境下,对象与request生命周期一致 .

    (4)session:Web环境下,对象与session生命周期一致.

    (5)global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。


    总结:绝大多数情况下,使用单例singleton(默认值),但是在与struts整合时候,务必要用prototype多例,因为struts2在每次请求都会创建一个新的Action,若为单例,在多请求情况下,每个请求找找spring拿的都是同一个action。

    5. Spring框架中的单例Beans是线程安全的么?

      Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。

    6、Spring如何处理线程并发问题?

    在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

    ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。

    ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

     7. 注解将对象注册到Spring容器当中,有几种注解方式?它们有什么区别吗?

    4种。配置在类上方的注解分别是:@Component()   普通的注解:用于三层外的bean的配置.   @Service()  业务层 .   @Controller() web层中使用 .     @Respository()//持久层.

    Spring框架最早出现的只有@Component()注解,但如果所有的对象都使用同一个注解,很难区分对象究竟属于哪一层架构。基于此,Spring又推出了@Service()、@Controller()、@Respository()三种注解,用于区分对象属于哪一层架构。但4种注解方式从功能上来说没有任何区别。

    8. 如何用注解的方式来完成属性注入?

      1. 在需要使用注解进行的时候,需要在配置文件中进行 开启注解扫描的配置

        <!--开启注解的扫描 扫描com.gs下所有的包-->
        <context:component-scan base-package="com.gs" />
         <!-- 开启Spring的注解的扫描:@Controller @Service @Repository @Component等 。默认存在-->
        <context:annotation-config />

          2. 按类型分可以分为值类型注入和引用类型注入。

        值类型注入可以通过@Value()注解来完成,该注解既可以声明在属性上,也可以声明在方法上,建议声明在方法上,但是更多的人会声明在属性上,因为更方便。

        引用类型注入可以通过三种注解方式来完成:            

          配置在属性上的注解

              @Resource   javax包下的一个注解,功能更加强大点

              @Autowired

              @Qualifier 属性注入时通知spring容器注入何种类型的对象(告诉spring容器对象的id(name))注意:在使用注解的方式下 及 一个接口下有多个实现类的时候 结合@Autowird一起使用

                

     9. Spring框架中,什么注解可以用来指定对象的作用范围?    和怎么导入其他spring配置文件

    答:@Scope(scopeName=”singleton”)。

    模块化配置,即分模块配置(导入其他spring配置文件)
    <beans>
        <import resource = “spring配置文件的全路径名” />
    </beans>

     10、Spring的自动装配。

    Spring中bean有三种装配机制,分别是:1. 在xml中显示配置、 2. 在java中显示配置,  3.  隐式的bean发现机制和自动装配。

    set注入和构造注入有时在做配置时比较麻烦。所以框架为了提高开发效率,提供自动装配功能,简化配置。

    Spring自动化装配可以借助@Autowired属性实现,以下是自动装配@Autowired属性的六个值的简要介绍:

      (1)no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。

      (2)byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。 

      (3)byType:通过参数的数据类型进行自动装配。   

          缺点:如果存在多个相同类型的bean对象,会出错。

                  如果属性为单一类型的数据,那么查找到多个关联对象会发生错误。

                       如果属性为数组或集合(泛型)类型,那么查找到多个关联对象不会发生异常。

      (4)constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。

      (5)autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。
      (6)default  :表示默认采用上一级标签的自动装配的取值。如果存在多个配置文件的话,那么每一个配置文件的自动装配方式都是独立的。 

    11.spring中 有哪些依赖注入方式?

      1. 使用属性的setter方法注入 . (可用于注入bean、字面量、集合;如果是新项目,建议使用Spring注解,因为一堆一堆的xml不好维护,同时功能也不如注解强大。)

    属性注入要求Bean提供一个默认的无参的构造函数(否则使用属性注入时将抛出异常),并为需要注入的属性提供对应的Setter方法Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值

    看下简单的例子:

    Car类 :定义了3个属性,并分别提供了对应的Setter方法。
    属性注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。
    Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值。
    package com.spring.model;
    
    public class Car {
        
        private int maxSpeed;
        private String brand;
        private double price;
        
        public int getMaxSpeed() {
            return maxSpeed;
        }
        //一定要写被注入对象的set方法
        public void setMaxSpeed(int maxSpeed) {
            this.maxSpeed = maxSpeed;
        }
        public String getBrand() {
            return brand;
        }
        public void setBrand(String brand) {
            this.brand = brand;
        }
        public double getPrice() {
            return price;
        }
        public void setPrice(double price) {
            this.price = price;
        }
        
        public void run(){
            System.out.println("brand:"+brand+",maxSpeed:"+maxSpeed+",price:"+price);
        }
    }
    View Code

    在Spring配置文件中对Car进行属性注入:

    <!-- 属性注入 -->
    <bean id="car" class="com.spring.model.Car">  
        <property name="maxSpeed" value="200"></property>
        <property name="brand" value="红旗CA72"></property>  
        <property name="price" value="200000.00"></property>
    </bean>
    View Code

    测试方法:

    @Test
    public void test(){
        //读取配置文件
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean的实例
        Car car=(Car) ctx.getBean("car");
        car.run();
    }
    View Code

     2.构造函数注入

    使用方式:
    第一,在类中,不用为属性设置setter方法,但是需要生成该类带参的构造方法。
    第二,在配置文件中配置该类的bean,并配置构造器,在配置构造器中用到了<constructor-arg>节点,该节点有四个属性:
    · index是索引,指定注入的属性,从0开始;
    · type是指该属性所对应的类型;
    · ref 是指引用的依赖对象(关联另一个bean)

    · value 当注入的不是依赖对象,而是基本数据类型时,就用value;

    看下简单的例子:
    User.java

    package com.gs.依赖注入;
    
    public class User {
        private String username;
        private String password;
        private Friend friend;
        
        public String getUsername() {
            return username;
        }
        
        public String getPassword() {
            return password;
        }
        
        public Friend getFriend() {
            return friend;
        }
        
        public User() {
            super();
        }
        public User(String username, String password, Friend friend) {
            super();
            this.username = username;
            this.password = password;
            this.friend = friend;
        }
        
        
    }
    View Code

       Friend.java

    package com.gs.依赖注入;
    
    public class Friend {
        private String name = "张三12345";
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Friend() {
            super();
        }
    
        public Friend(String name) {
            super();
            this.name = name;
        }
        
    }
    View Code

    Spring配置文件

    <bean id="user" class="com.gs.依赖注入.User">
        <!-- 通过索引注入值 -->
    <!--     <constructor-arg index="0" value="admin"/>
            <constructor-arg index="1" value="66666"/>
            <constructor-arg>
                    <bean class="com.gs.依赖注入.Friend"/>
            </constructor-arg>
    -->    
        
        <!-- 通过形参名称注入值 -->
    <!--    <constructor-arg name="username" value="李四"/>
            <constructor-arg name="password" value="123456"/> 
            <constructor-arg>
                    <bean class="com.gs.依赖注入.Friend"/>
            </constructor-arg>
    -->    
        
        <!-- 通过形参类型注入值 -->
            <constructor-arg type="java.lang.String" value="123456"/>
            <constructor-arg type="java.lang.String" value="lisi"/>
            <constructor-arg>
                    <bean class="com.gs.依赖注入.Friend"/>
            </constructor-arg> 
    </bean>
    View Code

    3.XML自动注入

    简化spring的配置文件:

     配置方案

      根节点beans   default-autowire="byName" 对当前配置文件的所有bean都生效

      子节点bean autowire="byType"只对当前bean生效

     

    1. 按照属性的名:bean里提供定义一个属性,提供 setXxx方法   

             default-autowire="byName"         配置文件的beanid必须和代码bean里的属性一致。

    2.  default-autowire="byType"  按照注入对象的类型,要求:类型只能配置一个实例     

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    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
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byType">
    
        <bean id="ud" class="com.gs.xmlauto.User"></bean>
        <bean id="us" class="com.gs.xmlauto.UserService">
            <property name="ud">
                    <ref bean="ud"/>
            </property>
        </bean>
        
        <bean id="ut" class="com.gs.xmlauto.UserServlet" autowire="byName">
            <property name="us" ref="utdsdas"></property>
        </bean>
        
        
        <bean id="utdsdas" class="com.gs.xmlauto.UserService">
                    <!--     <property name="us" ref="us"></property> -->
        </bean>
    </beans>
    View Code

    4. 全注解配置

            第一步:配置让Spring扫描类与支持注解

        <!-- 扫描器-->
          <context:component-scan base-package="com.gs.Allxmlauto"/>

        第二步:在类里面加上注解  @Controller @Service @Repository @Component等

     

    1.1. 调用名称两套方案:

    1.1.1. 方案一:使用@Autowired

    @Service

    public class UserService {

      @Autowired

      @Qualifier("userJdbcDao")

      private IUserDao userDao;

      public void save(){

      userDao.save();

      }

    }

     

    1.1.2. 方案二:使用@Resource

    @Service

    public class UserService {

      @Resource(name="userJpaDao")

      private IUserDao userDao;

      public void save(){

      userDao.save();

      }

    }

    12.Spring 框架中都用到了哪些设计模式?

    (1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;

    (2)单例模式:Bean默认为单例模式。

    (3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;

    (4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。

    (5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现--ApplicationListener。

    13. spring整合junit测试(spring创建容器)

    14. AspectJ和Spring与AOP的关系。

    AOP面向切面编程是一种编程思想,是对OOP面向对象编程的一种补充。Spring框架实现了AOP编程思想。而AspectJ框架也实现了AOP的功能,且其实现方式更为简捷,且还支持注解式开发

    所以,Spring又将AspectJ的对于AOP的实现也引入到了自己的框架中。后面使用AOP编程都是在Spring环境下使用AspectJ来进行的。

    15.  Spring的AOP理解

    AOP(面向切面编程)作为OOP(面向对象编程)的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于事务管理,日志管理,性能监测

    AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。

    16. 谈谈静态代理和 动态代理

    静态代理:就是在程序运行前就已经存在代理类的字节码文件,代理类和原始类的关系在运行前就已经确定。

        (缺点:如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。 )

    动态代理:动态代理类的源码是在程序运行期间通过JVM反射等机制动态生成,代理类和委托类的关系是运行时才确定的。

    Spring使用两种方式来完成动态代理:

    如果代理的类有接口,使用JDK的动态代理,如果代理的类没有接口,使用CGLIB的动态代理

    演示:JDK的动态代理

    UserDao.java

    package com.gs.spring.demo1;
    
    public interface UserDao {
        void save();
        void delete();
        void find();
        void update();
    }
    View Code

    UserDaoImpl.java

    package com.gs.spring.demo1;
    
    public class UserDaoImpl implements UserDao{
    
        @Override
        public void save() {
            System.out.println("保存用户");
            
        }
    
        @Override
        public void delete() {
            System.out.println("删除用户");
            
        }
    
        @Override
        public void find() {
            System.out.println("查找用户");
            
        }
    
        @Override
        public void update() {
            System.out.println("更新用户");
        }
    
    }
    View Code

    MyJDKProxy.java (代理类重点代码,要记)

    package com.gs.spring.demo1;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class MyJDKProxy implements InvocationHandler {
        //将被增强的对象传递到代理中
        private UserDao ud;
        public MyJDKProxy(UserDao ud) {
            this.ud=ud;
        }
        
        // 编写工具方法:生成代理:    
        public UserDao createProxy() {
            //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例    
            //(ClassLoader loader)第一个参数 指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器  
            //(Class<?>[] interfaces)第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口  
            //(InvocationHandler h)第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法  
            //根据传入的目标返回一个代理对象 
            UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), 
                    ud.getClass().getInterfaces(), this );
            return userDaoProxy;
        }
    /*invoke(Object proxy, Method method, Object[] args)
     * 三个参数:代理对象,正在执行的方法,方法的参数*/
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
            if("save".equals(method.getName())){
                System.out.println("权限校验================");
                }
            return method.invoke(ud, args);
        }
    }
    View Code

    SpringDemo1Test.java

    @Test
        public void Demo1() {
            UserDao ud = new UserDaoImpl();
            UserDao proxy = new MyJDKProxy(ud).createProxy();
            proxy.save();
            proxy.delete();
            proxy.find();
            proxy.update();
        }
    View Code

    演示:CGLIB的动态代理

    UserDaoImpl.java

    package com.gs.spring.demo2;
    
    public class UserDaoImpl {
        
        public void save() {
            System.out.println("保存用户");
            
        }
        
        public void delete() {
            System.out.println("删除用户");
            
        }
        
        public void find() {
            System.out.println("查找用户");
            
        }
    
        public void update() {
            System.out.println("更新用户");
        }
    
    }
    View Code

    MyCglibProxy.java(代理类重点代码,要记)

    package com.gs.spring.demo2;
    
    import java.lang.reflect.Method;
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    public class MyCglibProxy implements MethodInterceptor{
        //将被增强的对象传递到代理中
        private UserDaoImpl ud;
        public MyCglibProxy(UserDaoImpl ud) {
            this.ud=ud;
        }
        
        // 生成代理的方法:
        public UserDaoImpl createProxy() {
            // 创建 Cglib 的核心类:
            Enhancer enhancer = new Enhancer();
            // 设置父类:
            enhancer.setSuperclass(ud.getClass());
            // 设置回调:(类似于InvocationHandler对象)
            enhancer.setCallback(this);
            // 生成代理:
            UserDaoImpl proxy = (UserDaoImpl) enhancer.create();
            return proxy;
        }
    
        /*MethodProxy参数表示:  方法的代理*/
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            if("delete".equals(method.getName())) {
                Object obj = methodProxy.invokeSuper(proxy, args);
                System.out.println("日志记录================");
                return obj;
            }
            //相当于子类调用父类的方法
            return methodProxy.invokeSuper(proxy, args);    
        }
    }
    View Code

    SpringDemo2Test.java

    @Test
        public void Demo1() {
            UserDaoImpl ud = new UserDaoImpl();
            UserDaoImpl proxy = new MyCglibProxy(ud).createProxy();
            proxy.save();
            proxy.delete();
            proxy.find();
            proxy.update();
        }
    View Code

    17.spring中AOP名词解释

    Joinpoint(连接点):目标对象中,所有可以增强的方法.   Spring只支持方法的连接点

    Pointcut(切点):目标对象中,已经增强的方法。

    Advice(通知/增强):增强的代码

    Target(目标对象):被代理的对象。

    Introduction(引介):类层面的增强,为类添加一些属性和方法。

    Weaving(织入):将通知应用到切入点的过程

    Proxy(代理):将通知织入目标对象之后,形成代理对象。

    Aspect(切面):由切点和增强组成。在SpringAOP中,切面通过带有@Aspect注解的类实现。

     

     18. Spring通知有哪些类型?

    1. 前置通知 :在目标方法执行之前执行.
    2. 后置通知 :在目标方法执行之后执行
    3. 异常通知:在目标方法执行出现 异常的时候 执行
    4. 最终通知 :无论目标方法是否出现异常 最终通知都会 执行,类似于Java中的finally块。

    5. 环绕通知 :环绕通知可以在方法调用之前和之后自定义任何行为,并且可以决定是否执行连接点处的方法、替换返回值、抛出异常等等。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。

    五种通知的执行顺序为: 前置通知→环绕通知→正常返回通知/异常返回通知→后置通知。

     

    Xml版实现AOP

    <aop:config>
      <!-- 切点 --> <aop:pointcut expression="execution(* cn.itsource.aop.I*Service.*(..))" id="pointcut" />
      <!-- 切面 :一个切点 对应多个增强(通知) --> <aop:aspect ref="txManager"> <!-- 前置通知 --> <aop:before method="begin" pointcut-ref="pointcut"/> <!-- 后置通知 --> <aop:after-returning method="commit" pointcut-ref="pointcut"/> <!-- 异常通知 --> <aop:after-throwing method="rollback" pointcut-ref="pointcut"/> <!-- 最终通知 --> <aop:after method="close" pointcut-ref="pointcut" /> </aop:aspect> </aop:config>
    <!-- 
        环绕通知:有了环绕,就不需要用另外几个通知(会重复)
                   如果有两个以上的通知,建议使用环绕
     -->
    <aop:around method="around" pointcut-ref="pointcut" />

    注解版实现AOP

    配置文件

    <!-- 组件搜索 -->

    <context:component-scan base-package="com.gs" />

    <!-- 支持aop注解 -->

    <aop:aspectj-autoproxy />

    事务管理器

    @Component
    @Aspect //AOP的类注解
    public class TxManager {
        //设置切点
        @Pointcut("execution(* cn.itsource.aopanno.I*Service.*(..))")
        public void pointcut(){}
        //前置通知
        @Before("pointcut()")
        public void begin(){
            System.out.println("开启事务....");
        }
        //后置通知
        @AfterReturning("pointcut()")
        public void commit(){
            System.out.println("提交事务...");
        }
        //异常通知
        @AfterThrowing(pointcut="pointcut()",throwing="e")
        public void rollback(Throwable e){
            System.out.println("回滚事务....");
            System.out.println(e.getMessage());
        }
        //最终通知
        @After("pointcut()")
        public void close(){
            System.out.println("关闭资源....");
        }
    }

    环绕通知方法:

    //温馨提醒:如果要使用环绕通知的话,把其它几个通知的注解去掉(不然会出现重复)
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
        Object object = null;
        try {
            begin();
            object = joinPoint.proceed(); //执行相应的代码
            commit();
        } catch (Throwable e) {
            rollback(e);
        }finally{
            close();
        }
        return object;
    }

    参考文献: https://www.cnblogs.com/V1haoge/p/6106456.html

        https://blog.csdn.net/itcats_cn/article/details/81479185

        https://blog.csdn.net/a745233700/article/details/80959716

        https://blog.csdn.net/lz1170063911/article/details/79772474

        https://www.cnblogs.com/xiaoxi/p/5865330.html

        https://blog.csdn.net/yhl_jxy/article/details/78792414

  • 相关阅读:
    [na]wac无线控制器集中转发部署的几种情况
    [na]windows2008-AD域的安装
    [na]数据链路层&网络层协议小结截图版
    [na]tcp&udp层各协议小结
    [na]交换机接口文档
    [na]二层sw数据交换
    [na]wireshark排查打印机问题
    [na]ip包格式
    [na]ping提示&各系统默认的TTL值
    【VS开发】C++异常处理操作
  • 原文地址:https://www.cnblogs.com/gshao/p/10514052.html
Copyright © 2020-2023  润新知