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拿,可以确定
方式二:也是根据id和Class实例,可以确定(不需要强转)
方式三:根据类型,现在有两个类型相等,无法确定(报错)
//第一步:拿到资源配置文件 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. ApplicationContext是BeanFactory的子类,拥有更多的功能与方法。比如:提供了文本信息解析工具,包括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); } }
在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>
测试方法:
@Test public void test(){ //读取配置文件 ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); //获取bean的实例 Car car=(Car) ctx.getBean("car"); car.run(); }
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; } }
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; } }
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>
3.XML自动注入
简化spring的配置文件:
配置方案
根节点beans default-autowire="byName" 对当前配置文件的所有bean都生效
子节点bean autowire="byType"只对当前bean生效
1. 按照属性的名:bean里提供定义一个属性,提供 setXxx方法
default-autowire="byName" 配置文件的bean的id必须和代码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>
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(); }
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("更新用户"); } }
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); } }
SpringDemo1Test.java
@Test public void Demo1() { UserDao ud = new UserDaoImpl(); UserDao proxy = new MyJDKProxy(ud).createProxy(); proxy.save(); proxy.delete(); proxy.find(); proxy.update(); }
演示: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("更新用户"); } }
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); } }
SpringDemo2Test.java
@Test public void Demo1() { UserDaoImpl ud = new UserDaoImpl(); UserDaoImpl proxy = new MyCglibProxy(ud).createProxy(); proxy.save(); proxy.delete(); proxy.find(); proxy.update(); }
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