工具:idea、maven
第一部分 Spring
一、Spring IOC
1、名词解释
控制反转(Inversion of Control)或又叫依赖注入。
控制反转:将对象的创建销毁等动作交由Spring容器管理,传统是直接new,耦合度高。
依赖注入:不同对象之间的依赖关系交由Spring容器管理。
2、依赖注入
2.1、使用构造器
<!--class是具体的实现类的限定名-->
<bean id="constructDao" class="com.hjy.spring.inject.xml.construct.dao.ConstructDaoImp"/>
<bean id="constructService" class="com.hjy.spring.inject.xml.construct.service.ConstructServiceImp">
<constructor-arg index="0" ref="constructDao"/><!--index参数位置-->
</bean>
2.2、使用setter方法(偏多)
<bean id="constructDao" class="com.hjy.spring.inject.xml.setter.dao.ConstructDaoImp"/>
<bean id="constructService" class="com.hjy.spring.inject.xml.setter.service.ConstructServiceImp">
<!--name注入的对象-->
<property name="constructDao" ref="constructDao"/>
</bean>
二、Spring Bean
1、bean元素的属性
id、class、scope、、、、
2、bean的作用域
scope="singleton" 每次获取bean都返回同一个bean对象,spring容器管理
scope="prototype" 每次获取bean都创建并返回新的bean对象,spring容器不管理
3、bean的装配方式
3.1、xml装配
在xml文件中写好各个类的属性,适用于后期开发改动大
3.2、注解装配
在xml文件只需设置扫描包路径,spring容器会自动将这些类与关系纳入到管理中,前提是代码中需要设置注解
注解类型:
1.@Component,单单表示组件对象,一般用于pojo
2.@Repository,注解dao层
3.@Service,注解service层
4.@Controller,注解controller层
5.@Autowired,注解成员变量,方法和构造方法,通过类型来装配
6.@Resource,和@Autowired功能一样,但是实现不一样,通过名称来装配(java自带的)
7.@Qualifier,和@Autowired一起使用,存在多个实例时使用
三、Spring AOP
1、名词解释
AOP:即面向切面编程,与传统的OOP(面向对象编程)不同,AOP的优点是能将与业务逻辑代码无关的重复部分抽取到一个类中,使用时只需将这些抽取出的代码应用到业务逻辑代码即可,这样大大降低了代码的耦合度,在后期维护代码也很方便,注意,AOP和OOP相辅相成,没有替代性可言。
1.1、AOP的术语
1、 目标类target:就是我们需要增强的那个类 LaoZong.class
2、 代理类proxy:就是自定义的代理的对象 $Proxy0.class
3、 连接点joinPoint:程序执行的某个特定位置,Spring仅支持方法的连接点
eat(),sleep(),run()
4、 切入点pointCut:就是在目标类中实际增强的方法
eat()
5、 织入weave:就是将代理类中需要增强的方法放入到目标类中去执行的过程
将原方法与其他类的方法一起调用
6、 引介Introduction:引介是一种特殊的增强,它为类添加一些属性和方法(课程不使用)
7、 通知advice:将代理对象中的方法应用到目标类的过程中产生的结果。
8、 切面aspect:所有的切入点和代理对象的方法组成在一起构成了切面
2、JDK动态代理
前提:目标类有实现接口
1、创建目标类(业务逻辑类)
2、创建切面类(里面包含增强方法)
3、创建代理类(必须实现InvocationHandler接口)
public class MyProxy implements InvocationHandler {
//目标接口对象成员
private TestDao dao;
//建立代理对象与目标对象的关系,返回代理对象
public Object creatProxy(TestDao dao) {
//初始化目标对象
this.dao = dao;
//1.获取代理类的类加载器
ClassLoader classLoader = MyProxy.class.getClassLoader();
//2.获取目标类所实现的所有接口
Class[] clazz = dao.getClass().getInterfaces();
//3.返回增强后的目标对象(代理对象)
return Proxy.newProxyInstance(classLoader, clazz, this);
}
//回调方法,代理类使用目标类的方法时调用,proxy代理对象,method被执行的方法,args参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行业务逻辑前可以做的事");
//创建切面对象
MyAspect aspect = new MyAspect();
//前置通知
//传入目标对象和参数,调用业务方法
Object obj = method.invoke(dao, args);
//后置通知
return obj;
}
}
4.测试类
public static void main(String[] args) {
//1.创建目标对象
TestDao dao = new TestDaoImp();
//2.创建代理对象
MyProxy proxy = new MyProxy();
//3.创建被代理好的目标对象
TestDao dao = (TestDao)proxy.creatProxy(TestDao);
//4.执行被代理类增强了的目标类的方法
dao.query();
dao.insert();
}
3.CGlib动态代理
原理:采用字节码技术,对目标类生成子类,对子类增强代理.
1.创建目标类
2.创建切面类
3.创建代理类(实现MehtodInterceptor)
public class CGlibProxy implements MethodInterceptor {//cglib包的MethodInterceptor
//创建代理对象,建立代理对象和目标对象的关系
public Object creatProxy(Object target) {
//1.创建代理类对象
Enhancer enhancer = new Enhancer();
//2.设置目标类为代理类的父类
enhancer.setSuperclass(target.getClass());
//3.设置代理类对象调用本类的intercept方法(回调)
enhancer.setCallback(this);
//4.返回创建好的代理对象
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//1.创建切面类
MyAspect aspect = new MyAspect();
//2.前置增强
aspect.check();
aspect.exception();
//3.代理类执行目标对象方法
Object obj = methodProxy.invokeSuper(proxy, objects);
//4.后置增强
aspect.log();
return obj;
}
}
4.测试类
public class App {
public static void main(String[] args) {
//1.创建代理对象
CGlibProxy proxy = new CGlibProxy();
//2.创建目标对象
CGlibProxyDao dao = new CGlibProxyDao();
//3.创建增强了的目标对象(被代理了的目标对象)
CGlibProxyDao daoAdvice = (CGlibProxyDao)proxy.creatProxy(dao);
//4.执行增强了的方法
daoAdvice.query();
daoAdvice.insert();
daoAdvice.delete();
}
}
4.基于代理类的AOP实现
通知分类
前置通知:目标方法执行前增强
后置返回通知:目标方法执行成功后增强
环绕通知:目标方法执行前后增强
后置(最终)通知:目标方法执行后增强,相当于finally
异常通知:目标方法执行抛出异常后增强
引入通知:在目标类中增加方法和属性
1、创建目标类(需要接口)
2、创建切面类,并实现Method Interceptor接口(aopalliance包中的)
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//前置通知
check();
//目标类方法执行
Object proceed = methodInvocation.proceed();
//后置通知
log();
return proceed;
}
3、配置xml文件(重要)
<!--配置目标类bean-->
<!--配置切面类bean-->
<!--配置代理类bean-->
<bean id="proxyFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--指定目标对象-->
<property name="target" ref="myDao"/>
<!--指定代理类实现的接口-->
<property name="proxyInterfaces" value="myDao接口的限定名"/>
<!--指定切面类,织入通知-->
<property name="interceptorNames" value="myAspect"/>
<!--指定代理方式,默认false jdk代理-->
<property name="proxyTargetClass" value="true"/>
</bean>
4、测试类
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/hjy/spring/aop/proxyfactory/xml/applicationContext.xml");
MyDao myDao = (MyDao) applicationContext.getBean("proxyFactory");
myDao.search();
myDao.delete();
}
}
5、AspectJ开发(用的多)
XML
1、创建目标类
2、创建切面类
public class MyAspect {
/*
前置通知
JointPoint接口获得目标对象信息*/
public void before(JoinPoint joinPoint) {
System.out.println("前置通知,权限检查");
System.out.println("目标类对象:" + joinPoint.getTarget() + "被增强的方法:" + joinPoint.getSignature().getName());
}
/*
后置返回通知
JointPoint接口获得目标对象信息*/
public void afterReturning(JoinPoint joinPoint) {
System.out.println("后置返回通知,删除临时文件 ");
System.out.println("目标类对象:" + joinPoint.getTarget() + "被增强的方法:" + joinPoint.getSignature().getName());
}
/*
环绕通知
ProceedingJoinPoint是JoinPoint接口的子接口,代表可以执行的目标方法
返回值必须为Object
必须抛出Throwable异常
参数必须是ProceedingJoinPoint类型*/
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知开始,开启事务");
System.out.println("目标类对象:" + joinPoint.getTarget() + "被增强的方法:" + joinPoint.getSignature().getName());
//执行目标类的方法
Object proceed = joinPoint.proceed();
System.out.println("环绕通知结束,关闭事务");
return proceed;
}
/*异常通知*/
public void except(Throwable e) {
System.out.println("异常通知:" + e.getMessage());
}
/*后置(最终)通知*/
public void after() {
System.out.println("最终通知,释放资源");
}
}
3、配置xml文件
<!--定义目标类和切面类-->
<bean id="myDao" class="com.hjy.spring.aop.aspectj.xml.dao.MyDaoImp"/>
<bean id="myAspect" class="com.hjy.spring.aop.aspectj.xml.aspect.MyAspect"/>
<!--配置AOP-->
<aop:config>
<!--配置切面-->
<aop:aspect ref="myAspect">
<!--配置切入点,就是切面要增强的哪些方法-->
<!--第一个*代表返回类型不限制,第二个*代表这个包下所有类,注意dao下不能再有包文件,第三个*代表任意方法,(..)代表任意参数-->
<aop:pointcut id="myPointCut" expression="execution(* com.hjy.spring.aop.aspectj.xml.dao.*.*(..))"/>
<!--关联通知-->
<aop:after-returning method="afterReturning" pointcut-ref="myPointCut"/>
<aop:after method="after" pointcut-ref="myPointCut"/>
<aop:around method="around" pointcut-ref="myPointCut"/>
<aop:before method="before" pointcut-ref="myPointCut"/>
<aop:after-throwing method="except" pointcut-ref="myPointCut" throwing="e"/>
</aop:aspect>
</aop:config>
注意:通知的执行顺序和xml文件的配置顺序有关
4、测试
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("com/hjy/spring/aop/aspectj/xml/applicationContext.xml");
MyDao myDao = (MyDao)context.getBean("myDao");
myDao.search();
//myDao.delete();
}
}
注解(用的多)
1、创建目标类
2、创建切面类
@Aspect
@Component
public class MyAspect {
//需要定义一个空方法作为切点
@Pointcut("execution(* com.hjy.spring.aop.aspectj.annotation.dao.*.*(..))")
private void myPointCut() {
}
/*
前置通知
JointPoint接口获得目标对象信息*/
@Before("myPointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("前置通知,权限检查");
System.out.println("目标类对象:" + joinPoint.getTarget() + "被增强的方法:" + joinPoint.getSignature().getName());
}
/*
后置返回通知
JointPoint接口获得目标对象信息*/
@AfterReturning("myPointCut()")
public void afterReturning(JoinPoint joinPoint) {
System.out.println("后置返回通知,删除临时文件 ");
System.out.println("目标类对象:" + joinPoint.getTarget() + "被增强的方法:" + joinPoint.getSignature().getName());
}
/*
环绕通知
ProceedingJoinPoint是JoinPoint接口的子接口,代表可以执行的目标方法
返回值必须为Object
必须抛出Throwable异常
参数必须是ProceedingJoinPoint类型*/
@Around("myPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知开始,开启事务");
System.out.println("目标类对象:" + joinPoint.getTarget() + "被增强的方法:" + joinPoint.getSignature().getName());
//执行目标类的方法
Object proceed = joinPoint.proceed();
System.out.println("环绕通知结束,关闭事务");
return proceed;
}
/*异常通知*/
@AfterThrowing(value = "myPointCut()", throwing = "e")
public void except(Throwable e) {
System.out.println("异常通知:" + e.getMessage());
}
/*后置(最终)通知*/
@After("myPointCut()")
public void after() {
System.out.println("最终通知,释放资源");
}
}
3、配置xml文件
<!--扫描包,管理bean-->
<context:component-scan base-package="com.hjy.spring.aop.aspectj.annotation"/>
<!--启动支持-->
<aop:aspectj-autoproxy/>
4、测试
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("com/hjy/spring/aop/aspectj/annotation/applicationContext.xml");
MyDao myDao = (MyDao)context.getBean("myDao");
myDao.search();
//myDao.delete();
}
}
第四章:Spring的事务管理
1、Spring JdbcTemplate(了解)
2、编程式事务管理(了解)
3、声明式事务管理***
是通过AOP技术实现的事务管理,是对方法的拦截,对方法执行前创建一个事务,根据方法执行情况提交或者回滚事务
3.1 基于xml
1、创建dao层
@Repository("myDao")
public class MyDaoImp implements com.hjy.spring.transaction.xml.dao.MyDao {
@Autowired//注入jdbctemplate
private JdbcTemplate jdbcTemplate;
@Override
public int save(String sql, Object[] param) {
//增删改都是update方法,查询是query
return jdbcTemplate.update(sql, param);
}
@Override
public int delete(String sql, Object[] param) {
return jdbcTemplate.update(sql, param);
}
}
2、创建service层
@Service("myService")
public class MyServiceImp implements MyService {
@Autowired//注入dao层
private MyDao myDao;
@Override
public void call() {
//创建好要执行的sql语句
String saveSql = "insert into user values(?, ?, ?)";
String deleteSql = "delete from user";
//参数
Object[] param = {1, "HJY", "Man"};
//调用dao层的方法运行sql语句
myDao.delete(deleteSql, null);
myDao.save(saveSql, param);
//myDao.save(saveSql, param);
}
}
3、创建controller层
@Controller
public class MyController {
@Autowired//注入service层
private MyService myService;
public void call() {
myService.call();
}
}
4、配置xml文件(重点)
<!--扫描包-->
<context:component-scan base-package="com.hjy.spring.transaction"/>
<!--配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--MySql数据库驱动-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!--Url-->
<property name="url" value="jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC"/>
<!--username-->
<property name="username" value="root"/>
<!--password-->
<property name="password" value="123456"/>
</bean>
<!--Jdbc模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--1.要在bean.xml中创建spring的平台事务管理器 (DataSourceTransactionManager ) 注入dataSource-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2.我们需要创建一个 tx:advice 增强-->
<!--里面定义了筛选那些方法进行事务控制, 设置事务的隔离级别规则, 设置事务传播行为的规则 , 设置事务超时时间 设置事务是否只读..-->
<tx:advice id="myAdvice" transaction-manager="txManager">
<tx:attributes>
<!--
该增强中的规则:
name代表筛选那些方法收到事务的增强
isolation : 隔离级别 REPEATABLE_READ 可重复读
propagation: 传播行为
read-only: 是否为只读事务
timeout: 事务是否超时
-->
<tx:method name="translate" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" timeout="-1" />
<!--任意方法都使用这个事务-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--AOP,生成代理-->
<aop:config>
<!--切入点:实际要增强的方法-->
<aop:pointcut id="txPointCut" expression="execution(* com.hjy.spring.transaction.xml.service.*.*(..))"/>
<!--切面,将切入点与通知关联-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPointCut"/>
</aop:config>
3.2 基于annotation
前几个步骤和基于xml的事务管理是一样的,一般事务管理通常在service层,@Transactional注解可以添加在接口类、接口方法、普通类和普通方法上,注解放在接口上时只有使用基于接口代理时才会生效,注解放在类上代表类中所有的public方法都会有事务的属性
xml文件(把配置事务属性和aop部分去掉)
<!--为事务管理器注册注解驱动器-->
<tx:annotation-driven transaction-manager="txManager"/>
4、事务处理中捕获异常
如果在数据存储过程中发生异常,正常情况下必须进行事务回滚,也就是异常之前的数据库操作全部取消,但是如果将异常捕获,事务就不会发生回滚,这破坏了事务的特性,在捕获异常后加上下面的一句就行了
public void call() {
String saveSql = "insert into user values(?, ?, ?)";
String deleteSql = "delete from user";
Object[] param = {1, "HJY", "Man"};
try {
myDao.delete(deleteSql, null);
myDao.save(saveSql, param);
myDao.save(saveSql, param);
} catch (Exception e) {
System.out.println("主键重复,事务回滚");
//加上之后即使异常被捕获事务也会发生回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
总结
Spring这一章主要讲了IOC、AOP、事务控制这三个方面
其中IOC即控制反转,IOC将对象的创建延迟了,在代码中没有直接对对象的创建,而是交由给spring容器进行管理,减少了代码的冗余程度,IOC的实现有通过xml配置和注解Annotation配置这两种方式,bean的创建一般使用setter的方式。注解的类型分为@Componet(普通组件)、@Repository、@Service、@Controller、@Autowired等。
AOP即面向切面编程,是对OOP的补充,AOP将与业务代码无关的部分抽取出来放到一个切面类里,使用的时候创建业务类的代理类使得业务类的方法得以增强,代码的耦合度降低,这种方式让开发人员更专注与功能的实现,效率提高。AOP分为JDK动态代理(默认),CGlib动态代理和AspectJ框架代理,JDK代理前提是业务类实现了接口,需要自己创建代理类并实现InvocationHandler接口,CGlib代理采用的是底层字节码技术,用于对业务类生成一个子类,对其子类进行增强,创建的代理类需实现MethodInterceptor接口。AOP的使用过程中,AspectJ是使用的最多的,它是基于Java语言的框架,具体实现方式分为xml和annotation。
事务控制一般使用声明式的事务管理,controller调用service层调用dao层,在dao层需要注入spring的JdbcTemplate,声明式的事务管理也分为xml和annotation。事务的特性分为1、一致性(数据修改前后一致) 2、原子性(要么都做要么都不做) 3、隔离性(操作不相互影响) 4、持久性(写到硬盘)