1.Spring的junit测试集成
优点:不需要手动创建上下文,即不需要手动创建spring容器
package com.feng.test;
public class HelloService {
public void sayHello() {
System.out.println("Hello");
}
}
package com.feng.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
//junit整合spring的测试
@RunWith(SpringJUnit4ClassRunner.class)
//加载核心配置文件,自动构架spring容器,指定Spring容器的位置
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpringTest {
@Autowired
private HelloService helloService;
@Test
public void test() {
helloService.sayHello();
}
}
//applicaContex.xml
<bean id="helloService" class="com.feng.test.HelloService"></bean>
2.Spring 框架的一个关键组件是面向方面的编程(AOP)框架。面向方面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码的编写方式,如日志记录、审计、声明式事务、安全性和缓存等。
AOP是OOP(面向对象编程)的思想延续。
在 OOP 中,关键单元模块度是类,而在 AOP 中单元模块度是方面。依赖注入帮助你对应用程序对象相互解耦和 AOP 可以帮助你从它们所影响的对象中对横切关注点解耦。AOP 是像编程语言的触发物,如 Perl,.NET,Java 或者其他。
Spring AOP 模块提供拦截器来拦截一个应用程序,例如,当执行一个方法时,你可以在方法执行之前或之后添加额外的功能。
AOP原理:是代理思想,对目标对象进行代理(代码增强)
AOP 术语
这些术语并不特定于 Spring,而是与 AOP 有关的。
项 | 描述 |
---|---|
Aspect(切面) | 一个模块具有一组提供横切需求的 APIs。例如,一个日志模块为了记录日志将被 AOP 方面调用。应用程序可以拥有任意数量的方面,这取决于需求。 |
Join point(连接点) | 在你的应用程序中它代表一个点,你可以在插件 AOP 方面。你也能说,它是在实际的应用程序中,其中一个操作将使用 Spring AOP 框架。 |
Advice(通知) | 这是实际行动之前或之后执行的方法。这是在程序执行期间通过 Spring AOP 框架实际被调用的代码。 |
Pointcut(切入点) | 这是一组一个或多个连接点,通知应该被执行。你可以使用表达式或模式指定切入点正如我们将在 AOP 的例子中看到的。 |
Introduction(引入) | 在不修改代码的前提下,引用允许你添加新方法或属性到现有的类中。 |
Target object(代理的目标对象) | 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。 |
Weaving(织入) | Weaving 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。 |
通知的类型
Spring 方面可以使用下面提到的五种通知工作:
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知。 |
后置通知 | 在一个方法执行之后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在建议方法调用之前和之后,执行通知。 |
3.AOP编程底层实现机制
AOP就是要对目标进行代理对象的创建,Spring AOP是基于动态代理的,基于两种动态代理机制:JDK动态代理和CGLIB动态代理。
【面试题】动态代理和静态代理的区别:
动态代理:在虚拟机内部中,运行的时候,动态生成代理类(运行时生成,runtime生成),并不是真实存在的类,一般格式:Proxy$$(Proxy$$Customer);
静态代理:实际存在代理类(例如:struts2 Action的代理类 ActionProxy,struts2 的拦截器)。
JDK动态代理:针对目标对象的接口进行代理,动态生成接口的实现类!(必须有接口)
【过程要点】
- 必须对接口生成代理;
- 采用Proxy对象,通过newProxyinstance方法为目标创建代理对象。
该方法接受三个参数:
(1)目标对象类加载器
(2)目标对象实现的接口
(3)代理后的处理程序invocationHandler
3.实现InvocationHandler接口中invoke方法,在目标对象每个方法调用时,都会执行invok
//表示代理的目标接口
public interface ICustomerService {
//保存
public void save();
//查询
public int find();
}
//实现类
public class CustomerServiceImpl implements ICustomerService{
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("CustomerServiceImpl--save");
}
@Override
public int find() {
System.out.println("CustomerServiceImpl--find");
return 99;
// TODO Auto-generated method stub
}
}
代理工厂:有三种方案完成JDK动态代理
方案一:在内部实现new InvocationHandler(),指定匿名类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//方案一:在内部实现new InvocationHandler(),指定匿名类
//专门用来生成jdk的动态代理对象的--通用
public class JdkProxyFactory1 {
//成员变量
private Object target;
//注入target目标对象
public JdkProxyFactory1(Object target) {
this.target=target;
}
public Object getProxyObject() {
//参数1:目标对象的类加载器
//参数2:目标对象实现的接口
//参数3:回调方法对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
//如果是保存的方法,执行记录日志操作
if(method.getName().equals("save")) {
writeLog();
}
//目标对象原来的方法执行
Object object=method.invoke(target, args);//调用目标对象的某个方法,并且返回目标对象
return object;
}
});
}
//记录日志
protected static void writeLog() {
// TODO Auto-generated method stub
System.out.println("增强代码:写日志了。。。");
}
}
方案二:传递内部类的对象,指定内部类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//方案二:传递内部类的对象,指定内部类
//专门用来生成jdk的动态代理对象的--通用
public class JdkProxyFactory2 {
// 成员变量
private Object target;
// 注入target目标对象
public JdkProxyFactory2(Object target) {
this.target = target;
}
public Object getProxyObject() {
// 参数1:目标对象的类加载器
// 参数2:目标对象实现的接口
// 参数3:回调方法对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new MyInvocationHandler());
}
private class MyInvocationHandler implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
//如果是保存的方法,执行记录日志操作
if(method.getName().equals("save")) {
writeLog();
}
//目标对象原来的方法执行
Object object=method.invoke(target, args);//调用目标对象的某个方法,并且返回目标对象
return object;
}
}
//记录日志
protected static void writeLog() {
// TODO Auto-generated method stub
System.out.println("第二种");
System.out.println("增强代码:写日志了。。。");
}
}
方案三:直接使用调用类作为接口实现类,实现InvocationHandler接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//方案三:直接使用调用类作为接口实现类,实现InvocationHandler接口
//专门用来生成jdk的动态代理对象的--通用
public class JdkProxyFactory3 implements InvocationHandler {
// 成员变量
private Object target;
// 注入target目标对象
public JdkProxyFactory3(Object target) {
this.target = target;
}
public Object getProxyObject() {
// 参数1:目标对象的类加载器
// 参数2:目标对象实现的接口
// 参数3:回调方法对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
// 如果是保存的方法,执行记录日志操作
if (method.getName().equals("save")) {
writeLog();
}
// 目标对象原来的方法执行
Object object = method.invoke(target, args);// 调用目标对象的某个方法,并且返回目标对象
return object;
}
// 记录日志
protected static void writeLog() {
// TODO Auto-generated method stub
System.out.println("第三种");
System.out.println("增强代码:写日志了。。。");
}
}
import org.junit.Test;
//测试类
//目标:使用动态代理,对原来对方法进行功能增强,而无需更改原来对代码
//JDK动态代理:基于接口对(对象的类型,必须是实现接口)
public class SPpringTest {
@Test
public void testJdkProxy() {
//target(目标对象)
ICustomerService target=new CustomerServiceImpl();
//实例化注入目标对象
JdkProxyFactory1 jdkProxyFactory1=new JdkProxyFactory1(target);
JdkProxyFactory2 jdkProxyFactory2=new JdkProxyFactory2(target);
JdkProxyFactory3 jdkProxyFactory3=new JdkProxyFactory3(target);
//获取Proxy Object代理对象:基于目标对象类型的接口的类型的子类型的对象
ICustomerService proxy1=(ICustomerService) jdkProxyFactory1.getProxyObject();
ICustomerService proxy2=(ICustomerService) jdkProxyFactory2.getProxyObject();
ICustomerService proxy3=(ICustomerService) jdkProxyFactory3.getProxyObject();
//调用目标对象的方法
proxy1.save();
proxy2.save();
proxy3.save();
proxy1.find();
proxy2.find();
proxy3.find();
}
}