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

原文地址:https://www.cnblogs.com/gshao/p/10514052.html