一、控制反转
控制反转说白了就是将java对象注册到spring容器中,让spring 容器管理我们的对象。 对应的操作就是:
- 在xml中配置bean,将对象(bean)注册到spring 容器
- 使用注解@Component(同类@Controller,@Mapping、@Repository、@Service)、@Configuration(配合@Bean)同时配合包扫面@ComponentScan(),就能将对象(bean)注册到spring 容器
二、依赖注入
依赖输入就是将我们放到spring 容器的bean(对象)拿出来使用。注入到一个类中。这样我们就不需要通过new 来创建类的对象。 对应的操作就是:
1、在xml中注入:
<bean id="people" class="com.lihua.pojo.People" autowire="byName">
<property name="name" value="lihua"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
People 类
public class People {
private Cat cat;
private Dog dog;
private String name;
2、在java中注入
2.1、@Autowired和@Qualifier(“Cat1”)
@Autowired ,这种方式是根据byType进行注入,也就是根据类名(接口名)进行加载 比如:
@Autowired
private Cat cat;
注意:使用 @Autowired可能会报错,特别是注入一个接口类型时,因为接口会有很多实现类,在使用 @Autowired注入时,spring 容器不知道注入哪个实现类。
解决:我们可以结合@Qualifier(“Cat1”)注解,给接口指定一个实现类。这样@Autowired和@Qualifier(“Cat1”)配置使用我们就是通过byName来进行匹配。
比如:
- 接口:
package com.lihua.pojo;
import org.springframework.stereotype.Component;
public interface CatInterface {
void show();
}
- 实现类Cat1
package com.lihua.pojo;
import org.springframework.stereotype.Component;
@Component("Cat1")
public class Cat1 implements CatInterface {
public void show() {
System.out.println("cat1");
}
}
- 实现类Cat2
package com.lihua.pojo;
import org.springframework.stereotype.Component;
@Component("Cat2")
public class Cat2 implements CatInterface {
public void show() {
System.out.println("cat2");
}
}
- 测试类:
package com.lihua.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class Test {
@Autowired
@Qualifier("Cat2")
private CatInterface catInterface;
@org.junit.Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Test test = (Test)context.getBean("test");
test.catInterface.show();
}
}
2.2、@Resource(name = “Cat1”)
@Resource(name = “Cat1”) 等价于 @Autowired和@Qualifier(“Cat1”)
@Resource(name = “Cat1”) ,注解有两个属性分别是type和name
指定type属性,会根据byType进行匹配bean(即根据类名) 指定name属性,会根据byName进行匹配bean(根据id @Component(“Cat1”) 也就是里面的Cat1 ) 默认是byName匹配
3、属性注入的方式(spring容器是如何将值注入属性的)
Spring支持两种依赖注入方式,分别是属性注入和构造方法注入。用户不但可以将String,int等类型参数注入到Bean中,还可以将集合、Map类型注入到Bean中。
1、set方法注入
也就是在实体类里必须提供set方法,这样spring 容器才能通过set方法将值注入到属性中。
属性注入要求Bean提供一个默认的构造方法,并为需要注入的属性提供对应的set方法。Spring调Bean的默认构造方法创建对象,然后通过反射的方式调用set方法注入属性。
public class User {
private String username;
public void setUsername(String username){
this.username=username;
}
public void speak(){
System.out.println("my name is"+username);
}
}
<bean id="user" class="com.cad.domain.User">
<!--<property>对应一个属性,name是属性的名称,value是属性的值,ref用来引用其他bean-->
<property name="username" value="张三"></property>
</bean>
** 注意:set方法必须发和命名规范,否则 spring 容器无法匹配属性和set方法。**
2、有参构造注入
通过有参构照器注入,使用构造方法注入前提是Bean必须提供带参的构造方法。
public class User {
private String username;
public User(String username){
this.username=username;
}
public void speak(){
System.out.println("my name is"+username);
}
}
<bean id="user" class="com.cad.domain.User">
<!--<constructor-arg>标签对应构造方法的参数,name是参数名称,value是参数值-->
<constructor-arg name="username" value="jack"></constructor-arg>
</bean>
这里有一个小问题,如果类中有两个带参构造方法,我们应该怎样注入? < constructor-arg>标签有两个属性 index和type。 type是指定要注入的属性的类型,index指定注入的属性的顺序,从0开始。
三、IOC容器与spring容器
一般称BeanFactory为IoC容器,ApplicationContext为应用上下文,也称为Spring容器。 BeanFactory是Spring框架的基础,面向Spring本身,ApplicationContext面向使用Spring框架的开发者,几乎所有的应用我们都直接使用ApplicationContext而非底层的BeanFactory。
四、BeanFactory、ApplicationContext、WebApplicationContext
1、BeanFactory
BeanFactory是一个类工厂,和传统的类工厂不同,传统的类工厂仅负责构造一个类或几个类的实例。而BeanFactory可以创建并管理各种类的对象,Spring称这些被创建和管理的Java对象为Bean。
BeanFactory是一个接口,Spring为BeanFactory提供了多种实现,最常用的就是实现类XmlBeanFactory。 BeanFactory接口最主要的方法就是getBean(String beanName),该方法从容器中返回指定名称的Bean。 BeanFactory接口的功能通过其他实现它的接口不断扩展。
注意: XmlBeanFactory装在Spring配置文件并启动IoC容器。通过BeanFactory 启动IoC容器时,并不会初始化配置文件中定义的Bean,初始化创建动作在第一个调用时 。
package com.lihua.IOC;
import com.lihua.IOC.pojo.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
public class MyBeanFactory {
public static void main(String[] args) {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource resource = resolver.getResource("classpath:bean.xml");
BeanFactory bf = new XmlBeanFactory(resource);
User user = (User)bf.getBean("user");
user.setId(1);
System.out.println(user.getId());
}
}
2、ApplicationContext
ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要编程方式来实现,ApplicationContext中可以通过配置的方式来实现。
ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中加载配置文件。
package com.lihua.IOC;
import com.lihua.IOC.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class MyApplicationContext {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext app1 = new FileSystemXmlApplicationContext("C:\\Users\\15594\\IdeaProjects\\spring\\IOC容器的创建方式\\target\\classes\\bean.xml");
User beanByType = app.getBean(User.class);
System.out.println("byType:"+beanByType.toString());
User beanByName = (User) app.getBean("user");
System.out.println("byName:"+beanByName.toString());
}
}
3、WebApplicationContext
WebApplicationContext是专门为Web应用准备的,它允许以相对于Web根目录的路径中加载配置文件完成初始化工作。从WebApplicationContext中可以获取ServletContext的引用,整个WebApplicationContext对象作为属性放置到ServletContext中,以便Web应用环境中可以访问Spring应用上下文。
ConfigurableWebApplicationContext扩展了WebApplicationContext,允许通过配置方式实例化WebApplicationContext,定义了两个重要方法。
setServletContext(ServletContext servletcontext):为Spring设置ServletContext
setConfigLocation(String[] configLocations):设置Spring配置文件地址。
WebApplicationContext的初始化 在ServletContext对象创建的时候,借助监听器监听ServletContext对象的创建,然后初始化WebApplicationContext对象。Spring提供了这个监听器。ContextLoaderListener。我们只需要在web.xml配置监听器即可。
五、AOP
1、动态代理
动代理的核心是,代理对象不仅仅需要完成代理的任务,在完成这个任务的过程中代理对象也可以去做一些额外的事。 比如: 1、代理购票,代理者(代理对象)在代理购票的过程中,代理者还可以在买票前喝杯奶茶,买完票后去看一部电影。 2、代码中的例子:比如userMapper对象需要调用queryUserById(int id)这个方法查询一个用户。突然我们想要在查询到这个user后插入一条日志,在不改变原有的代码的前提下怎么做?我们已近知道动态代理的核心就是在完成主线任务的前后,可以完成一些支线任务。所有我们就可以通过动态代理来实现。
1.1、jdk动态代理
jdk动态代理需要代理的目标对象的类必须实现一个接口。这个接口里面存放的方法就是主线任务(主逻辑)
package com.lihua.AOP.JDKProxy;
public interface Animal {
public void run();
public void eat();
}
接口实现类,目标类(需要代理的类)
package com.lihua.AOP.JDKProxy;
public class Dog implements Animal {
public void run() {
System.out.println("跑");
}
public void eat() {
System.out.println("吃");
}
}
我们想在动物吃饭前后添加一些业务逻辑(支线任务,也就是一些方法、事务),我们为了方便调用将这些方法放到一个类中,当然也可以在不同类。
package com.lihua.AOP.JDKProxy;
public class MyAspect {
public void before(){
System.out.println("主人发出命令");
}
public void after(){
System.out.println("主人给予奖励");
}
}
在不改变原来代码的基础上,我们增加一个动态代理类,给目标类(方法)增加一些业务逻辑
package com.lihua.AOP.JDKProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyProxyFactory {
public static Animal createProxy(){
final Animal dog=new Dog();
final MyAspect aspect=new MyAspect();
Animal dogproxy=(Animal) Proxy.newProxyInstance(
MyAspect.class.getClassLoader(),
dog.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
aspect.before();
Object invoke = method.invoke(dog, args);
aspect.after();
return invoke;
}
}
);
return dogproxy;
}
}
使用动态代理生成的代理对象,通过调用代理对象来完成主业务逻辑和一些附加的业务逻辑
public class Test {
public static void main(String[] args) {
Animal animal1 = new Dog();
animal1.eat();
System.out.println("=======");
Animal animal2=MyProxyFactory.createProxy();
animal2.eat();
}
}
1.2、CGLib动态代理
使用JDK动态代理有一个很大的限制,就是只能为接口创建代理实例。 对于没有通过接口定义的类,如何动态创建代理实例呢?CGLib填补了这么空缺。
CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并织入横切逻辑。但是由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的private、final等方法进行代理
CGLib使用需要导入cglib.jar核心包和asm.jar依赖包,但是Spring 的Core.jar核心包中已经包含了这两个jar包,所以我们直接使用即可。
CGLib与jdk最大的区别就是CGLib目标对象不需要实现任何接口。 CGLib通过子类(这个子类不用写CGLib自己生成)的方式来实现代理,子类继承父类后就会根父类有一样的方法。
目标类
package com.lihua.AOP.CGLib;
public class Dog {
public void run() {
System.out.println("跑");
}
public void eat(int i) {
System.out.println("吃");
}
}
需要增加的业务逻辑
package com.lihua.AOP.CGLib;
public class MyAspect {
public void before(){
System.out.println("主人发出命令");
}
public void after(){
System.out.println("主人给予奖励");
}
}
动态代理类生成,代理对象
package com.lihua.AOP.CGLib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyProxyFactory {
public static Dog createProxy(){
Enhancer enhancer=new Enhancer();
final Dog dog=new Dog();
final MyAspect aspect=new MyAspect();
enhancer.setSuperclass(dog.getClass());
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
aspect.before();
Object obj=method.invoke(dog, objects);
aspect.after();
return obj;
}
});
Dog dogproxy=(Dog) enhancer.create();
return dogproxy;
}
}
测试
package com.lihua.AOP.CGLib;
import com.lihua.AOP.JDKProxy.Animal;
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(1);
System.out.println("=======");
Dog dog1 = MyProxyFactory.createProxy();
dog1.eat(1);
}
}
2、动态代理出现的一些问题
- 目标类的所有方法都添加了横切逻辑,而有时,我们只想对某些方法添加横切逻辑。
- 我们通过硬编码的方式指定了织入横切逻辑的连接点,显然不利于修改和维护。
- 我们纯手工的编写创建代理的过程,过程繁琐,为不同类创建代理时,需要分别编写不同的代码,无法做到通用。
spring AOP为我们完全解决了上述问题
3、spring AOP的一些概念
3.1、AOP术语(不用记、看完就理解了)
- 连接点(Joinpoint): 程序执行的某个特定位置:如类初始化前,类初始化后,某个方法调用前,方法调用后,方法抛出异常后。一个类或一段代码拥有一些具有边界性质的特定点,就被称为 “连接点”。Spring仅支持方法的连接点,仅能在方法调用前后、方法抛出异常时这些连接点织入增强。
举例:
package com.lihua.AOP.CGLib;
public class MyAspect {
public void before(){
System.out.println("在查询前插入一条日志");
}
public void queryUser(){
System.out.println("查询用户业务");
}
public void after(){
System.out.println("在查询前插入一条日志");
}
}
- 切点(Pointcut):每个程序都拥有很多连接点,如有一个两个方法的类,这两个方法都是连接点。如何定位到某个连接点上呢?AOP通过“切点”定位特定的连接点。例如:数据库记录是连接点,切点就是查询条件,用来定位特定的连接点。在Spring中,切点通过Pointcut接口描述,使用类和方法作为连接点的查询条件,Spring AOP的解析引擎负责解析切点设定的条件,找到对应的连接点。连接点是方法执行前后等具体程序执行点,而切点只定位到某个方法上,所以如果定位到具体的连接点,还需要提供方位信息,即方法执行前还是执行后。
举例: 比如用户登录模块,会有一些业务逻辑(方法)注册、修改密码、找回密码、登录…现在我们只需要对登录、修改密码这个两个方法添加一个日志增强(日志功能,记录用户登录、修改)。但是用户登录模块里面有很多方法,我们怎么只能给登录、修改方法织入日志增强?切点:可以理解为找到这些方法的位置。我们一般通过切点算法、切点表达式就行过滤方法 切点算法
package com.lihua.AOP.AOP.切面和切点;
import com.lihua.AOP.AOP.Dog;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import java.lang.reflect.Method;
public class MyPointcut extends StaticMethodMatcherPointcutAdvisor {
public boolean matches(Method method, Class<?> aClass) {
return method.getName().equals("eat");
}
@Override
public ClassFilter getClassFilter() {
return new ClassFilter(){
public boolean matches(Class<?> clazz) {
return Dog.class.isAssignableFrom(clazz);
}
};
}
}
切点表达式
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lihua.spring事务_声明式02.service.impl.BankServiceImpl.*(..))" />
-
增强/通知(Advice):增强是织入到目标类连接点上的一段程序代码。在Spring中,增强除了描述一段程序代码外,还拥有另一个和连接点相关的信息,就是执行点的方位。结合方位信息和切点信息,就可以找到特定的连接点。Spring提供的增强接口都是带方位名的:BeforeAdvice,AfterRetuningAdvice,ThrowsAdvice等。所以只有结合切点和增强,才能确定特定的连接点并织入增强代码。 增强也就是在原有的业务逻辑上增加一些额外是业务逻辑(比如日志),我们使用AOP织入增强可以做到在不改变原有的代码的前提下,增加业务逻辑 -
目标对象(Target):需要织入增强代码的目标类。 -
引介(Introduction):**引介是一种特殊的增强,**它为类添加一些属性和方法。这样,即使某个类没有实现某个接口,通过AOP的引介功能,我们可以动态的为该类添加接口的实现逻辑,让该类成为这个接口的实现类。 -
织入(Weaving):织入是将增强添加到目标类上具体连接点的过程。(将增强插入原有的业务逻辑的过程就叫织入) -
代理(Proxy):一个类被AOP织入增强后,就产生了一个结果类,它是融合了原类和增强逻辑的代理类。 -
切面(Aspect):切面由切点和增强组成,既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面,将定义的横切逻辑织入到切面指定的连接点中。切面一般由增强和切点组成
<bean id="advices" class="com.lihua.AOP.AOP.AspectJ.MyAdvice"></bean>
<aop:config proxy-target-class="true">
<aop:pointcut id="pointcut1" expression="execution(* *(..))"/>
<aop:aspect ref="advices">
<aop:before method="before" pointcut-ref="pointcut1"></aop:before>
<aop:after-returning method="after" pointcut-ref="pointcut1"></aop:after-returning>
</aop:aspect>
</aop:config>
3.2、增强类型
AOP联盟为增强定义了Advice接口,Spring支持五种类型的增强。
-
前置增强: org.springframework.aop.BeforeAdvice 代表前置增强,因为Spring目前只支持方法级的增强,所以MethodBeforeAdvice是目前可用的前置增强,表示在目标方法执行前实施置增强,而BeforeAdvice是为了将来扩展而定义的。 -
后置增强: org.springframework.aop.AfterReturningAdvice 代表后置增强,表示在目标方法执行后实施增强。 -
环绕增强: org.aopalliance.intercept.MethodInterceptor 代表环绕增强,表示在目标方法的执行前后实施增强。 -
异常抛出增强: org.springframework.aop.ThrowsAdvice 代表抛出异常增强,表示在目标方法抛出异常后实施增强。 -
引介增强: org.springframework.aop.IntroductionInterceptor 代表引介增强,表示在目标类中添加一些新的方法和属性。
4、spring AOP的使用
spring AOP的使用大体分为两种:
- ProxyFactoryBean:
通过spring 为我们提供的org.springframework.aop.framework.ProxyFactoryBean bean来实现。(不常用,但是能了解AOP原理) - AspectJ切面框架:
通过AspectJ切面框架实现,spring官方也提倡使用(常用)
4.1、 ProxyFactoryBean(不常用)
- 目标对象:
public interface Animal {
public void run();
public void eat();
}
public class Dog implements Animal{
public void run() {
System.out.println("跑");
}
public void eat() {
System.out.println("吃");
}
}
- 增强:
public class MyBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object obj) throws Throwable {
System.out.println("主人发出命令");
}
}
public class MyAfterAdvice implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("主人给予奖励");
}
}
public class MyMethodInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("主人发出命令");
Object proceed = methodInvocation.proceed();
System.out.println("主人给予奖励");
return proceed;
}
}
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Method method, Object[] args, Object target, Exception e)throws Throwable{
System.out.println(method.getName());
System.out.println(e.getMessage());
System.out.println("回滚事务");
}
}
public class ThrowsUserMapper {
public void queryUsers(){
throw new RuntimeException("查询users发生异常");
}
}
- 配置spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean id="target1" class="com.lihua.AOP.AOP.Dog"/>
<bean id="advice1" class="com.lihua.AOP.AOP.MyBeforeAdvice"/>
<bean id="beforeProxyService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<list value-type="com.lihua.AOP.AOP.Animal"/>
</property>
<property name="interceptorNames" value="advice1"/>
<property name="target" ref="target1"/>
</bean>
<bean id="target2" class="com.lihua.AOP.AOP.Dog"></bean>
<bean id="advice2" class="com.lihua.AOP.AOP.MyMethodInterceptor"></bean>
<bean id="surroundAdvice" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.lihua.AOP.AOP.Animal"></property>
<property name="interceptorNames" value="advice2"></property>
<property name="target" ref="target2"></property>
</bean>
<bean id="target" class="com.lihua.AOP.AOP.ThrowsUserMapper"></bean>
<bean id="advice" class="com.lihua.AOP.AOP.MyThrowsAdvice"></bean>
<bean id="proxyServiceThrows" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames" value="advice"/>
<property name="target" ref="target"/>
<property name="proxyTargetClass" value="true"/>
</bean>
</beans>
- 测试
public class Test {
@org.junit.Test
public void beforeAdvice() {
Animal dog=new Dog();
BeforeAdvice advice=new MyBeforeAdvice();
ProxyFactory pf=new ProxyFactory();
pf.setTarget(dog);
pf.addAdvice(advice);
Animal proxy=(Animal)pf.getProxy();
proxy.eat();
proxy.run();
System.out.println("当然我们也可以通过配置spring.xml文件来生成代理对象");
ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
Animal animalProxy = (Animal)context.getBean("beforeProxyService");
animalProxy.eat();
}
@org.junit.Test
public void afterAdvice(){
AfterReturningAdvice afterAdvice = new MyAfterAdvice();
Animal dog= new Dog();
ProxyFactory pf = new ProxyFactory();
pf.setTarget(dog);
pf.addAdvice(afterAdvice);
Dog proxyDog = (Dog)pf.getProxy();
proxyDog.eat();
}
@org.junit.Test
public void surroundAdvice(){
MethodInterceptor surroundAdvice = new MyMethodInterceptor();
Animal dog= new Dog();
ProxyFactory pf = new ProxyFactory();
pf.setTarget(dog);
pf.addAdvice(surroundAdvice);
Dog proxyDog = (Dog)pf.getProxy();
proxyDog.eat();
System.out.println("当然我们也可以通过配置spring.xml文件来生成代理对象");
ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
Animal animalProxy = (Animal)context.getBean("surroundAdvice");
animalProxy.eat();
}
@org.junit.Test
public void throwsAdvice(){
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
ThrowsUserMapper userMapper = (ThrowsUserMapper) ac.getBean("proxyServiceThrows");
try {
userMapper.queryUsers();
}catch (RuntimeException e){
System.out.println("处理抛出的异常");
}
}
}
增加切点筛选需要增强的方法:
切点算法
public class MyPointcut extends StaticMethodMatcherPointcutAdvisor {
public boolean matches(Method method, Class<?> aClass) {
return method.getName().equals("eat");
}
@Override
public ClassFilter getClassFilter() {
return new ClassFilter(){
public boolean matches(Class<?> clazz) {
return Dog.class.isAssignableFrom(clazz);
}
};
}
}
配置xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean id="targetDog" class="com.lihua.AOP.AOP.Dog"/>
<bean id="advice" class="com.lihua.AOP.AOP.切面和切点.MyBeforeAdvice"/>
<bean id="myPointcut" class="com.lihua.AOP.AOP.切面和切点.MyPointcut">
<property name="advice" ref="advice"/>
</bean>
<bean id="dogProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames" value="myPointcut"/>
<property name="proxyTargetClass" value="true"/>
<property name="target" ref="targetDog"/>
</bean>
</beans>
总的来说:使用 ProxyFactoryBean方式实现AOP需要三步:
- 编写增强类(方法)
- 编写切点算法(当然如果不需要筛选,可以不写)
- 将增强、目标对象、切点配置到ProxyFactoryBean中
- 生成代理对象
此外:我们发现上面的做法都是给一个个目标对象织入增强。当我们想要给很多目标对象织入一些增强时怎么办?要一个个织入吗?当然不用,我们可以通过以下配置让spring 帮我们筛选目标对象自动织入增强。配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean id="targetDog" class="com.lihua.AOP.AOP.AutoProxy.Dog"></bean>
<bean id="targetCat" class="com.lihua.AOP.AOP.AutoProxy.Cat"></bean>
<bean id="advice" class="com.lihua.AOP.AOP.AutoProxy.MyAfterAdvice"></bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*"></property>
<property name="interceptorNames" value="advice"></property>
<property name="optimize" value="true"></property>
</bean>
</beans>
4.2、 AspectJ切面框架(常用)
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。 Spring2.0之后增加了对AspectJ切点表达式的支持。@AspectJ是AspectJ1.5新增的功能,通过JDK的注解技术,允许开发者在Bean上直接通过注解定义切面。Spring使用和@AspectJ相同风格的注解,并通过AspectJ提供的注解库和解析库来处理切点。
1) 切点表达式
AspectJ中最重要的是切点表达式。
* 匹配任意字符,只匹配一个元素
.. 匹配任意字符,可以匹配多个元素 ,在表示类时,必须和*联合使用
+ 表示按照类型匹配指定类的所有类,必须跟在类名后面,如com.cad.Car+,表示继承该类的所有子类包括本身
切点表达式由切点函数组成,切点函数之间还可以进行逻辑运算,组成复合切点。
- && :与操作符。相当于切点的交集运算。xml配置文件中使用切点表达式,&是特殊字符,所以需要转义字符&;来表示。
- || :或操作符。相当于切点的并集运算。
- !:非操作符,相当于切点的反集运算。
- 切点表达式
语法:execution(<修饰符><返回类型><包.类.方法(参数)><异常>)
使用例子 :
- execution(public * *(..)) : 匹配目标类的所有public方法,第一个*代表返回类型,第二个*代表方法名,..代表方法的参数。
- execution(**User(..)) :匹配目标类所有以User为后缀的方法。第一个*代表返回类型,*User代表以User为后缀的方法
- execution(* com.cad.demo.User.*(..)) :匹配User类里的所有方法
- execution(* com.cad.demo.User+.*(..)) :匹配该类的子类包括该类的所有方法
- execution(* com.cad.*.*(..)) :匹配com.cad包下的所有类的所有方法
- execution(* com.cad..*.*(..)) :匹配com.cad包下、子孙包下所有类的所有方法
- execution(* addUser(Spring,int)) :匹配addUser方法,且第一个参数类型是String,第二个是int
除了execution外,还有以下表达式:
- args()
该函数接受一个类名,表示目标类方法参数是指定类时(包含子类),则匹配切点。
args(com.cad.User):匹配addUser(User user)方法等
- within()匹配类
语法:within(<类>)
within(com.cad.User):匹配User类下的所有方法
- target()
target()函数通过判断目标类是否按类型匹配指定类决定连接点是否匹配。
例如 target(com.cad.User):如果目标类类型是User没那么目标类所有方法都匹配切点。
- this()
this()函数判断代理对象的类是否按类型匹配指定类。
2) 增强类型
-
Before:前置增强。相当于BeforeAdvice的功能,方法执行前执行。 -
AfterReturning:后置增强。相当于AfterReturningAdvice,方法执行后执行。 -
Around:环绕增强。 -
AfterThrowing:异常抛出增强。 -
After:不管是抛出异常还是正常退出,该增强都会执行,类似于finally块。 -
DeclareParents:引介增强。
3) 通过配置xml实现
增强方法
public class MyAdvice {
public void before() {
System.out.println("主人发出命令");
}
public void after() {
System.out.println("主人给予奖励");
}
}
配置xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dog" class="com.lihua.AOP.AOP.Dog"></bean>
<bean id="cat" class="com.lihua.AOP.AOP.AutoProxy.Cat"></bean>
<bean id="advices" class="com.lihua.AOP.AOP.AspectJ.MyAdvice"></bean>
<aop:config proxy-target-class="true">
<aop:pointcut id="pointcut1" expression="execution(* *(..))"/>
<aop:aspect ref="advices">
<aop:before method="before" pointcut-ref="pointcut1"></aop:before>
<aop:after-returning method="after" pointcut-ref="pointcut1"></aop:after-returning>
</aop:aspect>
</aop:config>
</beans>
测试:
package com.lihua.AOP.AOP.AspectJ;
import com.lihua.AOP.AOP.AutoProxy.Cat;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAspectJ {
@Test
public void test(){
ApplicationContext app = new ClassPathXmlApplicationContext("AspectJ.xml");
Cat bean = (Cat) app.getBean("cat");
bean.eat();
}
}
4) 通过注解实现(推荐)
目标对象:
package com.lihua.AOP.AOP.AspectJ.注解实现AspectJ;
import org.springframework.stereotype.Component;
@Component("Dog")
public class Dog {
public void run() {
System.out.println("跑");
}
public void eat() {
System.out.println("吃");
}
}
增强
package com.lihua.AOP.AOP.AspectJ.注解实现AspectJ;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component("Advice")
@Aspect
public class MyAdvice {
@Before("execution(* com.lihua.AOP.AOP.AspectJ.注解实现AspectJ.Dog.run(..))")
public void before() {
System.out.println("主人发出命令");
}
@AfterReturning("execution(* com.lihua.AOP.AOP.AspectJ.注解实现AspectJ.Dog.e*(..))")
public void after() {
System.out.println("主人给予奖励");
}
}
配置xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.lihua.AOP.AOP.AspectJ.注解实现AspectJ"/>
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
测试:
package com.lihua.AOP.AOP.AspectJ.注解实现AspectJ;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("AspectJ1.xml");
Dog dog = (Dog)app.getBean("Dog");
dog.eat();
dog.run();
}
}
六、参考:
Spring 深入浅出核心技术 (一)
这篇文章AOP讲得很好: Spring 深入浅出核心技术(二)
|