1.AOP面向切面
关注点:切面(节点,可以认为是方法执行前的节点,执行后的节点,返回后节点)
横切式的编程思想
作用:减少组件间耦合性,提高代码复用性。
应用场景:性能优化、访问权限设置、事务管理、日志记录
切面(Aspect)
切面是切点和通知组成,通知和切点共同定义了切面的全部内容即:它是什么,在何时何处完成其功能;
连接点(Joinpoint)
连接点是在应用执行过程中能够插入切面的一个点,Spring仅支持方法的连接点,即仅能在方法调用前,方法调用后,方法抛出异常时及方法调用前后插入切面代码。
切点(Pointcut)
切点定义了在何处应用切面,AOP通过“切点”定位特定的连接点。切点相当于查询条件,一个切点可以匹配多个连接点。
通知(Advice)
切面的工作被成为通知,定义了切面是什么及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题,它应该在某个方法被调用之前?之后?等。
Spring切面可以应用5种类型的通知:
前置通知(Before)在目标方法被调用之前调用通知功能;
后置通知(After)在目标方法被完成之后调用通知功能,不关心方法的输出是什么;
环绕通知(Around advice)通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为;
异常通知(After-throwing)在目标方法抛出异常后调用通知;
返回通知(After-returning)在目标方法成功执行之后调用通知
目标对象(Target)
通知逻辑的织入目标类。如果没有AOP,那么目标业务类需要自己实现所有的逻辑,在AOP的帮助下,目标类只需要实现那些非横切逻辑的程序逻辑,而比如事务管理等这些横切逻辑就可以使用AOP动态织入特定的连接点上。
引入(Introduction)
引介是一种特殊的通知,为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,也可以动态地为该业务类添加接口的实现逻辑,使业务类成为这个接口的实现类。
代理(Proxy)
一个类被AOP织入通知后,就产生一个结果类,它是融合了原类和通知逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以可以采用与调用原类相同的方式调用代理类。
织入(Weaving)
织入是将通知添加到目标类的具体连接点上的过程.AOP就像一台织布机,将目标类,通知或引介编织到一起。 AOP有三种织入方式: ①编译期织入:切面在目标类编译时被织入,需要特殊的编译器;②类装载期织入:切面在目标类加载到JVM时被织入,需要特殊的类装载器;③动态代理织入:切面在应用运行的某个时刻呗织入,AOP容器会为目标对象动态创建一个代理对象;
Spring采用动态代理织入,AspectJ采用编译期织入和类装载期织入。
2.代理模式
(1)静态代理:
接口 , 客户类(实现接口) ,代理类(实现接口,构造器初始化买家)
缺点:代理类受接口的限制
(2)jdk动态代理:
接口,客户类(实现接口),代理类(实现 InvocationHandler接口)
//代理类织入
Proxy.newProxyInstance(buy.class.getClassLoader(), new Class[]{buy.class}, new agency(buyer))
ClassLoader loader, 类装载器
Class<?>[] interfaces, 接口数组
InvocationHandler h 代理对象
优点:代理类不受自定义接口限制
缺点:运行时需要自定义接口的class文件
必须基于接口上
(3)cglib动态代理:
客户类, 代理类(实现springFramwork...MethodInterceptor接口)
优点:不需要定义接口,极大降低了耦合性。
3.spring aop代理机制
Spring采用动态代理织入
(1)JDK动态代理
Spring的AOP的默认实现就是采用jdk的动态代理机制实现的。
自Java1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例。
//接口
public interface UserDao {
public boolean addUser();
public boolean updateUser();
public boolean delUser();
}
//实现类
public class UserDaoImpl implements UserDao {
//session验证 权限限制:user 是null 被拦截, user不为空操作
public User user;
@Override
public boolean addUser() {
System.out.println(user+"用户添加");
return false;
}
@Override
public boolean updateUser() {
System.out.println(user+"用户修改");
return false;
}
@Override
public boolean delUser() {
System.out.println(user+"用户删除");
return false;
}
}
//代理类
public class MyProxyFactory implements InvocationHandler{
private Object targetObj;//被代理的目标对象
public Object getInstance(Object targetObj){
this.targetObj=targetObj;
return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(),targetObj.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
UserDaoImpl daoImpl=(UserDaoImpl)targetObj;
Object res=null;
if(daoImpl.user!=null){ //user不为空 不拦截
res=method.invoke(targetObj, args);
}else{
System.out.println("没有权限,请登录");
}
return res;
}
}
//测试类
public class TestJdkProxy {
public static void main(String[] args) {
//生成代理对象
MyProxyFactory my=new MyProxyFactory();
//生成目标对象
UserDaoImpl dao=new UserDaoImpl();
/* User uu=new User();
uu.setUsername("张三");
uu.setPassword("123");
dao.user=uu;*/
//代办
UserDao res=(UserDao)my.getInstance(dao);
//调用
res.addUser();
res.updateUser();
res.delUser();
}
}
//需要定义接口,运行期间动态代理目标对象
(2)CGLib代理
JDK只能为接口创建代理实例,对于那些没有通过接口定义业务方法的类,可以通过CGLib创建代理实例。
CGLib采用底层字节码技术,可以为一个类创建子类,并在子类中采用方法拦截技术拦截所有父类方法的调用,这时可以顺势织入横切逻辑
//目标类,不需要接口
UserDaoImpl
//代理类
public class MyCglibProxyFactory implements MethodInterceptor{
private Object tagetObj;//被代理目标对象
public Object getInstance(Object tagetObj){
this.tagetObj=tagetObj;
Enhancer en=new Enhancer(); //cglib中,作用给代理对象生成父类
en.setSuperclass(tagetObj.getClass());//为目标对象生成父类
en.setCallback(this);
return en.create(); //创建代理
}
@Override
public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3) throws Throwable {
UserDaoImpl daoImpl=(UserDaoImpl)tagetObj;
Object res=null;
if(daoImpl.user!=null){ //user不为空 不拦截
res=method.invoke(tagetObj, arg2);
}else{
System.out.println("没有权限,请登录");
}
return res;
}
}
//测试类
public class TestCglib {
public static void main(String[] args) {
// 生成代理对象
MyCglibProxyFactory my=new MyCglibProxyFactory();
//目标对象
UserDaoImpl dao=new UserDaoImpl();
dao.user=new User();
//代理
UserDaoImpl res=(UserDaoImpl)my.getInstance(dao);
res.addUser();
res.updateUser();
res.delUser();
}
}
//不需要接口,运行期间动态代理目标对象
3.基于xml的aop编程
AspectJ采用编译期织入和类装载期织入(默认是jdk代理)
(1)pox.xml导入jar包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
(2) spring配置文件中
引入 aop schema 和location
xmlns:aop="http://www.springframework.org/schema/aop"
<!-- 打开aspectj代理 -->
<aop:aspectj-autoproxy/>
<!-- 打开注解扫描器 -->
<context:component-scan base-package="com.neuedu"/>
(3)创建切面 :切入点+通知(功能性的函数)
@Aspect
@Component("my")
public class MyAspect {
//切入点
@Pointcut("execution(* com.neuedu.dao.UserDaoImpl.*(..))") //查找的连接点是UserDaoImpl中任意方法
private void anyMethod(){}
//通知(前置、后置、最终、例外、环绕通知)
@Before("anyMethod()") //方法执行前的节点,拦截
public void beforeNotice(){
System.out.println("前置通知出发了。。。。");
}
@After("anyMethod()") //方法执行完return之前的节点处 触发
public void afterFinished(){
System.out.println("最终通知触发了---返回");
}
@AfterReturning("anyMethod()") //方法执行完并且返回值的节点处 ,触发
public void afterRetNotice(){
System.out.println("后置通知出发了。。。。");
}
@AfterThrowing("anyMethod()") //发生异常时触发
public void happenException(){
System.out.println("例外通知触发了。。。。。");
}
@Around("anyMethod()")
public Object aa(ProceedingJoinPoint point){
Object obj=null;
System.out.println("-----------环绕通知---------------------");
//放行
try {
obj=point.proceed();
} catch (Throwable e) {
System.out.println("。。。。。。。。环绕通知发生异常了。。。。。。");
}finally {
System.out.println("**环绕通知发生了例外通知***");
}
return obj;
}
}
(4)接口和目标类定义
public interface UserDao {
public boolean addUser();
public boolean updateUser();
public boolean delUser();
}
@Component("dao")
public class UserDaoImpl implements UserDao {
public User user;
@Override
public boolean addUser() {
System.out.println(user+"用户添加");
return false;
}
@Override
public boolean updateUser() {
System.out.println(user+"用户修改");
return false;
}
@Override
public boolean delUser() {
System.out.println(user+"用户删除");
return false;
}
}
(5) 测试:
public class Test {
public static void main(String[] args) {
ApplicationContext ctx= new ClassPathXmlApplicationContext("appSpring.xml");
UserDao dao=(UserDao)ctx.getBean("dao");//必须转成接口类型
dao.addUser();
dao.delUser();
}
}
//说明
切入点: @Pointcut("execution")
Execution表达式语法
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)?
除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。
execution(public * *(..))
匹配所有目标类的public方法,第一个*代表返回类型,第二个*代表方法名,而..代表任意入参的方法;
execution(* *To(..))
匹配目标类所有以To为后缀的方法,第一个*代表返回类型,而*To代表任意以To为后缀的方法;
execution(* com.neuedu.UserService.*(..))
匹配UserService接口的所有方法,第一个*代表返回任意类型,com.neuedu.UserService.*代表UserService接口中的所有方法;
execution(* com.neuedu.*(..))
匹配com.neuedu包下所有类的所有方法
execution(* com.neuedu..*(..))
匹配com.neuedu包、子孙包下所有类的所有方法,“..”出现在类名中时,后面必须跟“*”,表示包、子孙包下的所有类;
execution(* com..*.*Dao.find*(..))
匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀.
通知:
前置通知@before
最终通知@after
后置通知@afterRetuning
例外通知@afterThrowing
环绕通知@around 前置到后置整个节点 如果发生异常不再执行例外通知
ProceedingJoinPoint对象
proceed()放行
4.基于注解的切面
public class MyAspect {
public void beforeNotice(){
System.out.println("---before------");
}
public void afterFinished(){
System.out.println("----after------");
}
public void afterRetNotice(){
System.out.println("----finally-------");
}
public void happenException(){
System.out.println("---exception---------");
}
public void aa(ProceedingJoinPoint point){
System.out.println("-----------around--------------------");
}
}
//配置文件
<context:component-scan base-package="com.neuedu"></context:component-scan>
<bean name="myas" class="com.neuedu.controllor.MyAspect"></bean>
<!-- aop代理 -->
<aop:aspectj-autoproxy/>
<!--声明切面 配置 -->
<aop:config>
<!-- 切面-->
<aop:aspect id="as111" ref="myas">
<!-- 切入点 -->
<aop:pointcut expression="execution(* com.neuedu.dao.UserDaoImpl.*(..))" id="aa"/>
<!--通知 -->
<aop:before method="beforeNotice" pointcut-ref="aa"/>
<aop:after method="afterFinished" pointcut-ref="aa"/>
<aop:after-returning method="afterRetNotice" pointcut-ref="aa"/>
<aop:after-throwing method="happenException" pointcut-ref="aa"/>
<!-- <aop:around method="aa" pointcut-ref="Apoint"/> -->
</aop:aspect>
</aop:config>
5.spring jdbc
(1)pom.xml中导包
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.5.0</version>
</dependency>
(2)spring.xml配置数据源
bean 中name与id都是唯一标识某个bean,name的值可以是多个,id值只能是1个,
name和id可以同时出现,同时出现是id值和name值不能相同。
开发中通常使用name
<bean name="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/shop"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<!--初始连接数 -->
<property name="initialSize" value="10"></property>
<!--最大空闲连接 -->
<property name="maxIdle" value="20"></property>
<!-- 最大连接数 -->
<property name="maxTotal" value="100"></property>
<!--最小空闲连接 -->
<property name="minIdle" value="5"></property>
</bean>
(3)开发DAo层