Spring
AOP
-
什么是AOP
AOP(Aspect Oirented Programming),即面向切面编程,可以利用AOP对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的重用性以及开发效率。通俗的讲:AOP可以实现在不修改源码的基础上,在主干功能中添加新功能。
如在原有的登录逻辑上添加权限判断(是否管理员)时,不使用AOP可能要去原有代码逻辑中添加判断语句修改,而使用AOP可以不修改源代码从而添加权限判断模块来完成。 -
AOP的底层原理
AOP的底层使用动态代理模式。当被代理类存在被代理类接口时,使用JDK动态代理创建接口实现类代理对象,来实现AOP。当被代理类没有实现被代理类接口时,使用CGLIB动态代理创建子类的代理对象。 -
JDK动态代理
由静态代理可知,动态代理的关键之处有两点:
- 如何根据被代理类对象动态的创建出代理类对象:使用Proxy.getProxyInstance(ClassLoader loader , Class<?>[] interfaces ,InvocationHandler handler)
方法
- 如何使用代理类动态的调用被代理类中的方法:使用InvocationHandler接口的实现类
//被代理类接口
public interface User{
void login();
void regist();
}
//被代理类
public class UserImpl{
@Override
public void login(){
System.out.print("login方法");
}
@Override
public void regist(){
System.out.print("regist方法");
}
}
//使用ProxyFactory工厂类创建动态代理对象
public class ProxyFactory{
public Object getProxyInstanc(Object object){
MyInvacationHandler handler = new MyInvocationHandler();
handler.bind(object);
return Proxy.newProxyInstance(object.getClass().getClassLoader,object.getClass().getInterfaces(),handler);
}
}
//使用InvocationHandler实现类完成方法的调用
public class MyInvocationHandler implements InvocationHandler{
private Object object = null;
public void bind(Object object){
this.object = object;
}
public Object invoke(Object proxy , Method method , Object[] args) throws Throwable{
//执行一些需要优化的操作
//print(...)
Object object = method.invoke(obj , args);
//执行一些需要优化的操作
return object;
}
}
-
AOP中的一些术语
-
连接点:指的是类中那些方法可以被增强,这些方法就被称之为连接点
-
切入点:指的是实际真正被增强的方法,称为切入点
-
通知(增强):实际增强的逻辑部分称为通知(增强)
通知有多种类型,分为:- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知(类似于finally,是一个无论如何都会执行的通知)
-
切面:是一个动作,指的是把通知应用到切入点的全过程
-
-
AspectJ框架
AspectJ框架不是Spring的组成部分,他是一个独立的AOP框架,一般把AspectJ框架和Spring一起使用进行AOP操作。基于AspectJ实现AOP操作可以通过注解方式和XML配置文件方式实现。 -
切入点表达式:用于告知Spring对哪些类中的哪些方法进行增强,格式为:
execution([权限修饰符][返回类型][类全路径][方法名]([参数列表]))
,如execution(* com.jarvis.dao.UserDao.login(..))
-
基于AspectJ注解实现AOP操作
- 创建增强类和被增强类 ,并添加自动创建对象注解(@Component注解等)
- 在XML文件中进行开启组件扫描
<context:component-scan base-package=""></context:component-scan>
- 在增强类上添加注解@AspectJ,表示Spring将使用该类生成代理对象
- 在XML文件中开启生成代理对象:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 配置不同类型的通知(使用不同的注解对代理类中的不同方法进行标注)
- @Before(value=“切入点表达式”):前置通知注解
- @After:代表最终通知注解
- @AfterReturning:代表后置通知注解
- @AfterThrowing:异常通知注解
- @Around:环绕通知注解
@Component
@AspectJ
public class UserProxy{
//前置增强
@Before(value="execution(* com.jarvis.bean.User.login(..)")
public void before(){}
//后置增强
@AfterReturning(value="execution(* com.jarvis.bean.User.login(..)")
public void afterReturning(){}
//环绕增强,这个方法有些不同,需要在参数列表中添加参数ProceedingJoinPoint
@Around(value="execution(* com.jarvis.bean.User.login(..)")
public void around(ProceedingJoinPoint point){
//环绕前置通知操作
point.proceed();
//环绕后置通知操作
}
//异常通知
@AfterThrowing(value="execution(* com.jarvis.bean.User.login(..)")
public void afterThrowing(){}
//最终通知
@After(value="execution(* com.jarvis.bean.User.login(..)")
public void after(){}
}
- 抽取相同的切入点
当有多个通知需要添加到相同的切入点时,我们可以自定义一个方法来将这个切入点抽取出来,使用到@Pointcut注解,当使用这个切入点时,使用该方法名代替即可:
@Pointcut(value="execution(....)")
public void pointForBalaBala(){}
@Before(value = "pointForBalaBala()")
public void before(){}
-
对多个增强类设置增强类优先级
当有多个类对同一切入点进行增强时,可以使用@Order(数字类型value)注解对他们进行优先级设置,value越小优先级越高 -
完全注解开发
只需要创建配置类实现开启组件扫描以及开启生成代理类即可,添加以下注解即可完成完全注解开发:@Configuration
@ComponentScan
@EnableAspectJAutoProxy(proxyTargetClass=true)
-
使用配置文件实现AOP
其实使用配置文件实现AOP也非常简单,只需要在配置文件中完成以下步骤即可:- 配置增强类和被增强类的Bean管理
- 开启组件扫描和代理类生成
- 设置切入点和切面
<bean id="user" class=""></bean>
<bean id="userProxy" class=""></bean>
<context:component-scan base-packages=""></context:compnent-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<aop:config>
<!-- 设置切入点 -->
<aop:pointcut id="point1" expression="execution(...)">
<!-- 设置切面 -->
<aop:aspect id="aspect1" ref="userProxy">
<!-- 设置通知 -->
<aop:before method="" point-ref="point1"/>
</aop:aspect>
<aop:config>
JdbcTepmlate
-
JdbcTemplate的概念梳理
什么是JdbcTemplate,是Spring框架对JDBC进行封装,使用其方便实现对数据库的操作。也就是说,他相当于完成了两方面的封装:- JDBCUtils:也即使用数据库连接池获取数据库连接(properties====》dataSource =====》Connection)并对其该数据库连接进行操作的过程
- BaseDAO:即使用DBUtils来对数据库连接进行操作的过程(QueryRunner=====》操作sql语句)
在使用JDBCTemplate时,我们只需要在XML配置文件中完成对数据库连接池的配置,以及对JDBCTemplate对象Bean管理的配置,这样就可以使用IOC+JDBCTemplate对数据库进行操作了:
<!-- 当然也可以使用之前提到过的context:property-placeholder获取外部文件的方式 -->
<bean id="dataSource" class="...">
<property name="driverClassName" value=""></property>
<property name="username" value=""></property>
<property name="url" value=""></property>
<property name="password" value=""></property>
</bean>
<bean id="jdbcTemplate" class="">
<property name="dataSource" ref="dataSource"></property>
</bean>
-
使用JdbcTemplate操作数据库
- 更新操作:使用JdbcTemplate对象的update(String sql , Object…args)方法,传入SQL语句及sql注入参数即可
- 查询操作:根据不同的需求可以使用不同的方法:
- 查询返回某个值:如查询
COUNT(*)
,可以使用JdbcTemplate对象的queryForObject(String sql , Class<T> requiredType)
方法——queryForObject(sql,Integer,class)
- 查询返回对象:使用JdbcTemplate对象的重载方法
queryForObject(String sql , RowMapper<T> rowMapper, Object...args)
,如User user = queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),id);
- 查询返回集合:使用JdbcTemplate对象的
query(String sql , RowMapper<T> rowMapper, Object...args)
,如List<User> list = query(sql,new BeanPropertyRowMapper<User>(User.class),id);
- 查询返回某个值:如查询
-
使用JdbcTemplate批量操作数据库
当一次所需执行相同操作的数据量较大时,可以使用批量操作方法batchUpdate(String sql , List<Object[]> args)
,参数sql为SQL语句,args为需要批量操作的多组sql注入值。一般来说,使用批量操作进行添加删除时会更加方便。