a)什么是动态代理:相对于静态代理 b)Jdk动态代理: i.新建接口Task如下:
public interface Task { public void doSomeThing(); }ii.新建类Person实现Task接口,并覆盖抽象方法doSomething如下:
public class Person implements Task{ public void doSomeThing() { System.out.println("========"); } }iii.新建类Helper,并实现JDK的代理InvocationHandler如下:
public class Helper implements InvocationHandler{ private Object target; public Helper(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("帮忙送花"); Object obj = method.invoke(target,args); System.out.println("帮忙订餐"); return obj; } }iv.新建测试类,代码如下
public class Main { public static void main(String[] args) { Task p = new Person();//创建纯洁的业务类对象 Helper helper = new Helper(p); Task superPerson = (Task)Proxy.newProxyInstance(p.getClass().getClassLoader(),p.getClass().getInterfaces(),helper); superPerson.doSomeThing(); } }c)Cglib动态代理: i.新建任务类Person,注意不需要接口,代码如下:
public class Person { public void doSomeThing() { System.out.println("=============="); } }新建代理类CglibHepler,实现MethodInterceptor,代码如下:
public class CglibHelper implements MethodInterceptor{ //创建代理 public Object getProxy(Class<?> clzss) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clzss); // 设置回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } //拦截方法 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("帮忙买花"); Object obj = methodProxy.invokeSuper(o,objects); System.out.println("帮忙订餐"); return obj; } }iii.新建测试类,代码如下
public class Main { public static void main(String[] args) { CglibHelper cglibHelper = new CglibHelper(); Person p = (Person)cglibHelper.getProxy(Person.class); p.doSomeThing(); } }d)区别:jdk动态代理和cglib动态代理的区别 i.实现接口不同,jdk实现InvocationHandler,cglib实现MethodInterceptor ii.Jdk动态代理的业务类必须实现上级接口,cglib可以直接代理类,无需上级接口。 iii.Cglib方式代理开发效率高过jdk动态代理,所以我们spring中都引用了cglib包 动态代理使用场景:一般出现在有前置或者后置操作的方法上,包括但不限于:调用时间记录、日志记录、事务开启与提交、sqlsession开闭
a)AOP(Aspect Oriented Programming):面向切面编程。它是一种设计模式,用于实现一个系统中的某一个方面的应用。如果有一段同样的业务代码出现在不同的业务方法中,出现的频率极高,那么我们就要考虑将这个业务提取出来形成一个切面。 b)为什么需要AOP:功能组件(增删改查)因为加入了大量的附加的业务逻辑而变得无比庞大和笨重。一个添加客户的功能应该是关心的是如何将客户添加进数据库,而不是关心它是不是安全或支持事务。 c)关于如何设计:面向切面编程的设计应该遵循这个理念,即将公有的业务功能(例如:调用时间监控、日志记录、session开闭、事务管理、登录验证)抽取出来,形成一个切面,然后进行编程开发。 d)AOP术语: i.切面(Aspect)描述的是一个应用系统的某一个方面或领域(增强型业务),例如:日志,事务,权限检查,session开闭、时间记录等。 ii.连接点(Joinpoint)连接点是应用程序执行过程中插入切面的点,这些点可能是方法的调用前、异常抛出后、结果返回后等时间点。 iii.通知(Advise)通知它是切面的具体实现,它表示切面的行为,例如在日志的切面应用中,日志通知包含了实现日志功能的具体代码,例如向日志文件写入日志信息等。 iv.切入点(Pointcut)切入点指定了通知应当应用在那些连接点上,通知可以应用到AOP框架支持的任何的连接点,例如:根据方法名来确定切入点等,它指定了那些连接点需要被通知。 v.引入(Introduction)引入允许你为已存在的类添加新的方法和属性。 vi.目标对象(Target)目标对象是指被通知的对象,它是一个普通的业务对象(纯洁的业务类),如果没有AOP那么它其中可能包含大量的非核心业务逻辑代码,例如日志,事务等,而如果使用了AOP则其中只有核心的业务逻辑代码。 vii.代理(Proxy)代理是指将通知应用到目标对象后形成的新的对象。它实现了与目标对象一样的功能,不同的是它添加了通知的应用功能例如日志,事务等,对用户而言它与目标对象是一样的。 织入(Weaving)织入是指将切面应用到目标对象从而建立一个新的代理对象的过程,切面在指定的接入点被织入目标对象中,织入一般可发生在对象的编译期,类装载期或运行期,Spring的AOP采用的是运行期织入。
修改pom文件中的支持,如下所示
properties> <spring-version>4.3.6.RELEASE</spring-version> </properties> <dependencies> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.4</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.4</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId> com.springsource.org.aspectj.weaver </artifactId> <version>1.6.8.RELEASE</version> </dependency> </dependencies>b)使用xml配置方法进行aop编程 i.声明dao接口和实现类,代码略。但是要在Impl类上面加上@Componet注解,声明此类被spring容器托管。 ii.声明service接口和实现类,代码略。但是要在Impl类上加上@Componet注解,声明此类被spring容器托管。同时要在Impl类的dao应用上面加上@Autowired,声明自动注入。 iii.新建类TransDemo,声明4个函数前置、后置、环绕、异常抛出。这个类就是我们的代理,代码如下:
public class TransDemo { public void begin(){ System.out.println("start tansaction"); } public void end(){ System.out.println("end transaction"); } public void around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("begin transaction"); joinPoint.proceed(); System.out.println("commit transaction"); } public void thorwExption() throws Exception { throw new Exception("制造一个异常"); } }iv.创建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" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" 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-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" > <context:component-scan base-package="com.qf.aopdemo"></context:component-scan> <bean id="transDemo" class="com.qf.aopdemo.tansaction.TransDemo"></bean> <aop:config> <aop:pointcut expression="execution(* com.qf.aopdemo.service.*.*.*(..))" id="p1" /> <aop:aspect ref = "transDemo"> <!--前置通知--> <aop:before method="begin" pointcut-ref="p1" /> <!--后置通知--> <aop:after-returning method="end" pointcut-ref="p1"/> <!--环绕通知--> <aop:around method="around" pointcut-ref="p1"/> </aop:aspect> </aop:config> </beans>注意,这个配置文件头部加了aop的支持信息。 新建测试类,代码如下:
public class Main { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml"); UserService userService = applicationContext.getBean(UserService.class); UserInfo user = new UserInfo(); user.setAge(19); user.setName("yangxin"); userService.addUser(user); userService.deleteUser(user); } }c)使用注解的方法进行aop编程 新建基于注解的切面类,内容如下:
@Aspect public class TransDemo2 { //定义切点 @Pointcut(value="execution(* com.qf.aopdemo.*.*.*(..))") public void point(){ } @Before(value="point()") public void before(){ System.out.println("transaction begin before"); } @AfterReturning(value = "point()") public void after(){ System.out.println("transaction commit after"); } @Around("point()") public void around(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("transaction begin around"); joinPoint.proceed(); System.out.println("transaction commit around"); } }ii.修改spring-aop.xml,内容如下:
<bean id = "transactionDemo2" class = "com.qf.aopdemo.tansaction.TransDemo2" /> <aop:aspectj-autoproxy />运行测试类,不需要修改任何代码 4.Springaop的常见注解 a)@Pointcut b)@Before c)@AfterReturning d)@AfterThrowing e)@After f)@Around 5.织入点语法: a)execution(修饰符 返回类型 方法名(形参列表)):声明这是一个织入点。 b)参数模式稍微有点复杂: i.()匹配了一个不接受任何参数的方法, ii.(…)匹配了一个接受任意数量参数的方法(零或者更多) iii.模式()匹配了一个接受一个任何类型的参数的方法 iv.模式(,String)匹配了一个接受两个参数的方法,第一个可以是任意类型, 第二个则必须是String类型 6.织入点语法举例: a)任意公共方法的执行: execution(public * (…)) b)任何一个名字以“set”开始的方法的执行: execution( set*(…)) c)AccountService接口定义的任意方法的执行: execution(* com.qf.service.AccountService.(…)) d)在service包中定义的任意方法的执行: execution( com.qf.service..(…)) e)在service包或其子包中定义的任意方法的执行: execution(* com.qf.service….(…))