<!doctype html>Spring5笔记css
Spring是一个轻量级的开源的javaee框架html
Spring目的:能够解决企业应用开发的复杂性java
String组成部分:核心部分node
总结一句话:Spring就是一个轻量级的数据反转(IOC)和面向切面编程(AOP)的框架mysql
Spring bootweb
Spring cloudspring
由于使用的人多,大多数都在使用SpringBoot进行快速开发,学习SpringBoot的前提,须要彻底掌握Spring及SpringMVC!承上启下的做用!sql
弊端:发展了过久以后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”数据库
建立maven工程,普通Java工程编程
导入坐标
只须要导入一个Spring Context,经过传递依赖就能够把其余的jar所有导入进来
<dependencies>
<!--经过maven窗体依赖,导入依赖的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
在resources目录下导入log4j.properties
编写包,编写Service接口和实现类
在resouces目录下编写配置文件(推荐名为:"applicationContext.xml")
xxxxxxxxxx
<!--文档声明-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
编写测试方法,调方法
小提示:ApplicationContext工厂类 按住快捷键Ctrl+H 能够查看该类的结构
xxxxxxxxxx
//先建立Spring的IOC的工厂,加载src目录下的配置文件,把配置文件中的类建立成对象,存储到IOC容器中
ApplicationContext ac = new FileSystemXMlApplicationContext("加载某个磁盘上的文件");
//ApplicationContext ac = new ClassPathXmlApplicationContext("resources目录下配置文件");
//从容器中获取对象
UserService us = (UserService)ac.getBean("配置文件bean标签的id属性获取该对象");//注意:强转,使用接口接收
//调用对象的方法
us....
Bean标签的属性
id:--> 对象在IOC容器中惟一的名称,要求编写的时候是惟一的
class: --> 管理的类的全路径(包名+类名),经过Java反射技术帮助建立实例对象,建立后存到IOC容器当中,经过id来拿对象
scope: --> 建立后对象的生命周期(做用范围)
singLeton --> 默认的:单例的,IOC容器中只会存在一个实例的 (通常默认使用)
prototype --> 多例的:(每次获取 都会建立新的实例对象)
xxxxxxxxxx
//验证单例和多例的生命周期
//在service编写构造方法输出内容 配置文件修改单例和多例查询结果 是否先输出构造参数仍是先输出从容器中获取对象以前的内容
init-method ,当bean被载入到容器的时候调用init-method属性指定的方法
destroy-method ,当bean从容器中删除的时候调用destroy-method属性指定的方法
通常会用来完成销毁工做: destroy-method = "service实现类方法"
xxxxxxxxxx
<bean id="" class="" scope="" init-method="" destroy-method=""></bean>
框架提供建立bean对象的三种方式,基本上都使用默认方式
xxxxxxxxxx
<bean id="user" class="...."/>
xxxxxxxxxx
//建立StaticFactory实体类
//建立静态create(自定义名User)方法 返回值Service这个类
public class StaticFactory{
//静态工厂方式
public static UserService createUser(){
sout("经过静态工厂的方式建立....")
return new UserServiceImpl();
}
}
//配置文件中 "经过StaticFactory类的creatrUser这个静态类获取的对象"
//优势:编写不少业务逻辑 权限校验...
<bean id="user" class="包名+StaticFactory类" factory-method="createUser" />
//测试类
...
xxxxxxxxxx
//建立Dfactory实体类
//动态工厂方式
public class Dfactory{
//静态工厂方式
public static UserService createUser(){
sout("实例化工厂的方式建立....")
return new UserServiceImpl();
}
}
<bean id="user" class="包名+Dfactory类" factory-method="createUser" factory-bean="dfactory" />;
//测试类
...
把全部配置都写在一个配置文件中,太乱,spring支持多配置文件方式
xxxxxxxxxx
//加载多个配置文件
ApplicationContext ac = new ClassPathXmlApplication("applicationContext.xml","application...");//CTRL+P 参数能够有多个 能够加载多个配置文件
xxxxxxxxxx
<!-- 引入其余的配置文件 -->
<import resource="applicationContext2.xml"/>
ApplicationContext工厂:建立对象都是由工厂来实现的,读取配置文件,底层使用反射机制来建立对象
ApplicationContext这个接口底层有2个实现类,Ctrl+H查看
使用工厂,获取IOC容器中的对象 工厂对象.getBean("bean标签的id别名");
加载多个配置文件,获取工厂时,参数能够给多个
或在主配置文件中,增长引入其余的配置文件
IOC和DI的概念
引用类型注入:
xxxxxxxxxx
//将dao和service都存放到容器中
//在Service里面建立Dao成员属性
//给成员属性提供一个set方法
//最后修改bean标签
<bean id="user" class="...">
<property name="这个类的属性名" ref="引用(bean标签的id属性)"/>
</bean>
基本类型注入
xxxxxxxxxx
//将dao和service都存放到容器中
//在Service里面建立Dao成员属性
//给成员属性提供一个set方法
//最后修改bean标签
<bean id="user" class="...">
<property name="属性名" value="成员属性基本类型的值"/>
</bean>
xxxxxxxxxx
<!--基本数据类型注入方式-->
<bean id="service" class="cn.dong.service.impl.UserServiceImpl">
<property name="dao" ref="userDao" />
<property name="name" value="张三" />
</bean>
总结:
xxxxxxxxxx
//写实体类 建立构造方法
//配置文件配置bean
<bean id="惟一的名" class="..">
<constructor-arg name="属性的名称" value="基本类型" ref="引用类型"/>
<constructor-arg name="属性的名称" value="基本类型" ref="引用类型"/>
<constructor-arg name="属性的名称" value="基本类型" ref="引用类型"/>
</>
xxxxxxxxxx
<!--属性构造器方式注入值-->
<bean id="user" class="cn.dong.domain.User">
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="20"/>
</bean>
本身通常写程序用的比较少,可是使用多个框架整合的时候,必定得会写
xxxxxxxxxx
//写实体类 建立set方法 tostring 数组,集合
//配置文件(给集合属性注入值)
<bean id="" class="">
//数组
<property name="属性名称">
<array>//给数组传值
<value>张三</value>//基本类型的数组
<value>李四</value>//基本类型的数组
//<ref></ref>引用类型的数组
<array/>
</property>
//list集合
<property name="属性名称">
<list>//给list集合传值
<value>张三</value>//泛型为基本类型
<value>李四</value>//泛型为基本类型
//<ref></ref>泛型为引用类型
<list/>
</property>
//map集合
<property name="属性名称">
<map>//给list集合传值
<entry key-ref="" value/>//泛型为引用类型
<entry key="" value=""/>//泛型为基本类型
<entry key-ref="" value=""/>//泛型为基本类型
<map/>
</property>
//属性配置文件 实体类 private Properties properties;
<property>
<props>
<prop key="key...">值</prop>
</props>
</property>
<bean/>
@Value(value = "字符串或数值") //在属性上增长
xxxxxxxxxx
//注意:使用配置文件方式依赖注入,须要提供set方法
//使用注解注入值能够不须要set方法
"张三") (
private String name;
value="29") (
private int age;
xxxxxxxxxx
//也不用提供set方法 (经常使用)
//按类型(和id没有任何关系,容器中有对象就自动装配注入)自动装配的注解
private User user;
//按id名称的方式注入 @Qualifier不能单独使用,须要Autowired一块儿使用
value="id名称") (
//这一个顶第二个两个 (偶尔用)
name = "id名称") //Java提供的注解,按名称注入对象,属性名称是name (
xxxxxxxxxx
//建立普通的Java的maven工程
//引入坐标,
<dependencies>
<!--经过maven窗体依赖,导入依赖的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--日志实现-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<!--日志-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--链接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<!--MySql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
</dependencies>;
//建立数据库 建立表结构 id主键 用户名 money钱 添加3条数据
...;
//新建包,实体类对应数据库,dao,service
//dao写一个查询全部数据的方法
...
//service...
//测试方法
...
xxxxxxxxxx
//新建编写spring核心配置文件 log4j配置文件
//配置service和dao的bean标签 依赖注入...
//dao 链接池对象注入到spring 配置链接池
<bean id="" class="...">
<property name="driverClassName" value"" />
<property name="url" value"" />
<property name="username" value"" />
<property name="password" value"" />
</bean>;
//在dao提供DateSource成员属性 建立set方法 privete DataSource dataSource
<bean id="" class="...">
<property name="dataSource" ref="dataSource" />
</bean>
IOC注解的方式依赖没有变化
编写接口和实体类
在须要管理的类上增长@Compoent注解
xxxxxxxxxx
value = "user") //组件,做用:把当前类使用IOC容器进行管理,若是没有指定名称,默认使用类名,首字母是小写,或者本身指定名称 (
public class UserServiceImpl implents UserService{}
//spring核心配置文件 多配置文件也能够
<!-- 开启注解扫描 -->;
<context:component-scan base-package="cn.dong包名"/>
以上注解@Component... =
xxxxxxxxxx
value="annotation") (
public class AnnotationServiceImpl implements AnnotationService {
public void showInfo() {
System.out.println("显示信息!!");
}
}
@Value(value = "字符串或数值") //在属性上增长
xxxxxxxxxx
//注意:使用配置文件方式依赖注入,须要提供set方法
//使用注解注入值能够不须要set方法
"张三") (
private String name;
value="29") (
private int age;
xxxxxxxxxx
//也不用提供set方法 (经常使用)
//按类型(和id没有任何关系,容器中有对象就自动装配注入)自动装配的注解
private User user;
//按id名称的方式注入 @Qualifier不能单独使用,须要Autowired一块儿使用
value="id名称") (
//这一个顶第二个两个 (偶尔用)
name = "id名称") //Java提供的注解,按名称注入对象,属性名称是name (
xxxxxxxxxx
value = "singleton") //默认值,单例的 (
value = "propotype") //多例的 (
xxxxxxxxxx
//@PostConstruct 至关于init-method
public void init(){ sout("初始化"); }
//@PreDestroy 至关于destroy-method
public void destroy(){ sout("销毁"); }
使用以上注解须要在Spring的配置文件进行注解扫描,不然无法注入到IOC容器,报错空指针异常
xxxxxxxxxx
<!--开启注解扫描(组件扫描)-->
<context:component-scan base-package="cn.dong.service"/>
纯注解的方式是微服务架构开发的主要方式,因此也是很是的重要。纯注解的目的是换掉全部的配置文件。可是须要编写配置类。
编写配置类SpringConfig,包SpringConfig,替换掉applicationContext.xml配置文件
xxxxxxxxxx
//声明当前类是一个配置类
value = {"cn.dong","..."})//配置扫描的包 (
public class ...{}
编写测试方法
//编写程序,加载配置类
//建立工厂,加载配置类
ApplicaitonCOntext ac = new AnnotationConfigApplicaitonContext(配置类.class);
ac.getBean(....
范例:
xxxxxxxxxx
value="showInfo")//加载类到IOC容器 (
public class UserServiceImpl implements UserService {//Service业务层
public void showInfo() {
System.out.println("业务层显示信息.....");
}
}
//声明当前类是一个配置类
value="cn.dong") //配置扫描的包 (
public class SpringConfig {
}
junit.Test .
public void show(){//测试类
//建立工厂,加载配置类
ApplicationContext ac = new AnnotationConfigApplicationContext(UserServiceImpl.class);
//使用工厂获取对象
UserServiceImpl service=(UserServiceImpl) ac.getBean("showInfo");
service.showInfo();
}
@import注解 String的配置文件能够分红多个配置的,编写多个配置类,用于导入其余配置类
xxxxxxxxxx
@Impoer(value={xxx.class,xxx.class})
public class SrpingConfig{}
@Bean 只能写在方法上,代表使用此方法建立一个对象,对象建立完成保存到IOC容器中,(用于解决链接池的对象管理等,不少类都不是本身写的,只是拿来用不能随意更改,不能加注解,就得使用bean注解)
xxxxxxxxxx
管理IOC,管理链接池对象
/**
* 建立链接池对象,返回对象,把该方法建立后的对象存入到链接池中,使用@Bean注解解决
*/;
name="dataSource")//name惟一的 (
public DataSource source(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("...");
dataSource.setUsername(".....");
return dataSource;
}
同等于 Spring配置文件中,配置链接池对象;
<bean id="..." ..>
<property name="" value="..."/>
</bean>
xxxxxxxxxx
持久层;
//Spring模板
name="template") (
public JdbcTemplate template(){
//实例化JdbcTemplate对象 Spring模板
JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
return template;
}
测试类;
public void show(){
//加载配置类,建立工厂
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
//使用工厂对象获取对象
JdbcTemplate template =(JdbcTemplate) ac.getBean("template");
//写sql语句
String sql = "select count(*) from user";
//数据操做
Integer i=template.queryForObject(sql, Integer.class);
System.out.println(i);
}
经常使用注解(类加载到IOC容器)
这四个注解使用方法,和结果都是同样的,可是仍是推荐哪一个领域写哪一个注解!
依赖注入注解
基本类型依赖注入
引用类型依赖注入
对象生命周期注解
初始化和销毁注解
IOC纯注解方式
引入多个配置类
xxxxxxxxxx
@Configuration //声明配置类
public class SpringConfig{}
@Configuration //声明配置类
//扫描指定的包结构
@ComponentScan(value="cn.dong")
//引用新的配置类
Import(valueu = {SpringConfig2.class})
public class SpringConfig2{}
@Bean注解(重要)
只能写在方法上,代表使用此方法建立一个对象,对象建立完成保存到IOC容器中,(用于解决链接池的对象管理等,不少类都不是本身写的,只是拿来用不能随意更改,不能加注解,就得使用bean注解)
快速入门
xxxxxxxxxx
<!-- 整合单元测试 -->
<bean id="user" class="cn.dong.User"></bean>
xxxxxxxxxx
//运行单元测试
value = SpringJunit4ClassRunner.class) (
//加载类路径下的配置文件
value = "classpath:Spring配置文件路径") (
public Class Test{
//测试哪个对象,就把该对象注入进来,在测试环境下,可使用注解方式注入测试的对象
private User user;//依赖注入的测试对象
public void show(){
//...
}
}
xxxxxxxxxx
//建立配置类
//声明配置类
//扫描包结构
value = "cn.dong要扫描的路径") (
public class SpringConfig(){
}
//建立测试类
SpringJunit4ClassRunner.class)//运行单元测试 (
classes = SpringConfig.class)//加载配置类 (
public class Test(){
//按类型依赖注入
private User user;
public void show(){
user...
}
}
xxxxxxxxxx
测试类
//运行单元测试
value = SpringJUnit4ClassRunner.class) (
//加载路径下的配置文件
value="classpath:applicationContext.xml") (
public class Test3 {
//依赖注入的测试对象
private User user;
//Junit测试
public void show(){
user.setName("颤三");
System.out.println(user);
}
}
User实体类
public class User {
为何要使用Spring整合的Junit单元测试?
测试类增长如下注解
在测试类中,增长"依赖注入的测试对象(接口)" 直接使用对象调方法便可 (千万不要忘记: 依赖注入的测试对象 增长@Autowired 自动装配对象到IOC容器管理)
建立maven的普通工程,引入开发坐标,跟原来如出一辙
建立包,实体类,业务层,持久层...
导入配置文件
业务层建立2个帐户,持久层建立1个帐户
问题来了:业务层若是有一个帐户建立失败,就不能给数据库添加数据,就应该事务回滚,须要事务管理
开启事务 要用Connection接口,发现dao也须要用到Connection接口,并且还必须用到业务层的Connection接口
xxxxxxxxxx
//业务层
sout("业务层:保存两个帐号");
//开启事务 要用Connection接口,发现dao也须要用到Connection接口,并且还必须用到业务层的Connection接口
工具类.startTransaction();
//第一种方法:方法的参数传递,设计以前参数须要一个Connection接口,谁调用谁传递 (太麻烦)
//第二种方法:线程绑定 能够从当前线程中拿到直接绑定的Connection (优先使用)
//执行操做
try{
dao.seve(user1);
//模拟异常
...
dao.seve(user2);
//提交事务/回滚事务
工具类.commit();
} catch(...) {
//回滚事务打印异常信息
工具类.rollback();
}finally{
//关闭资源(归还链接)
工具类.close();
}
编写持久层dao;
//获取到链接
Connection conn = 工具类.getConnection();
//编写sql
...;
//预编译
PreparedStatement stmt = conn.prepareStatement(sql);
//设置值
stme.setString(1,user.getName());
stmt.setInt(2,user.getMoney());
//执行操做
stmt.executeUpdate();
//关闭资源 conn不能关闭
stmt.close();
xxxxxxxxxx
业务层
//DI依赖注入Dao
UserDaoImpl dao;
public void setDao(UserDaoImpl dao) {
this.dao=dao;
}
/**
* 保存2个帐户
* @param user1
* @param user2
*/
public void save(User user1, User user2) {
//开启事务:使用第二种方法“线程绑定”
try {
AffairUtils.startTransaction();
//执行操做
dao.save(user1);
dao.save(user2);
//提交事务
AffairUtils.commit();
} catch (Exception e) {
//打印错误信息
e.printStackTrace();
//提交回滚
AffairUtils.rollback();
} finally {
//归还链接
AffairUtils.close();
}
}
持久层;
public void save(User user) throws SQLException {
//获取链接
Connection conn=AffairUtils.getConnection();
//编写sql
String sql="insert into `user`.`user` (`name`, `money`) values (?, ?)";
//预编译
PreparedStatement stmt=conn.prepareStatement(sql);
//设置值
stmt.setString(1, user.getName());
stmt.setInt(2, user.getMoney());
//执行操做
stmt.executeUpdate();
//关闭资源
stmt.close();
}
测试类:
public void show(){
//加载配置文件,建立工厂
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用工厂对象获取对象
UserService service =(UserService) ac.getBean("userService");
//建立用户
User user1 = new User("熊大",1000);
User user2 = new User("熊二",2000);
//保存
service.save(user1,user2);
}
使用到的事务工具类: AffairUtils.java
业务层开启事务 要用Connection接口,发现持久层也须要用到Connection接口,并且还必须用到业务层的Connection接口
以上代码的业务层很是繁琐,在真正开发中,业务层只须要关注业务逻辑,不须要关注其余,因此就须要进行,业务层的方法加强
加强方法的手段
xxxxxxxxxx
//建立JdkProxy类
//传入目标对象,生成该对象的代理对象,返回,对目标对象的方法进行加强
//获取代理对象,返回,加强目标对象的方法
public static Object getProxy(UserService userService){
/**
* 使用Jdk的动态代理生成代理对象
*/
Object proxy = Proxy.newProxyInstance(类加载器,传入对象的实现的接口要字节码对象,回调函数匿名内部类);
//类加载器 JdkProxy.class.getClassLoader()
//传入对象实现了哪些接口的字节码对象userService.getClass().getInterfaces()
new InvocationHandler(){//invoke方法参数Object proxy , Method method , Object[] args
try{
//开启事务
//对目标对象的方法进行加强
Object result = method.invoke(userService,args);
//提交事务
}回滚事务...归还链接
return result;
}
return proxy;
}
xxxxxxxxxx
测试类:
public void show(){
//其余代码省略,请看上方
//使用工厂对象获取对象
UserService service =(UserService) ac.getBean("userService");
//生成代理对象
Object proxyObj= JdkProxy.getProxy(userService);
//强转
UserService proxy = (UserService) proxyObj;
//调用代理对象方法(进行保存到数据库)
proxy.save(user1,user2);
}
范例:
xxxxxxxxxx
业务层;
//DI依赖注入Dao
UserDaoImpl dao;
public void setDao(UserDaoImpl dao) {
this.dao=dao;
}
/**
* 保存2个帐户
* @param user1
* @param user2
*/
public void save(User user1, User user2) throws SQLException {
//开启事务:使用第二种方法“线程绑定”+Jdk动态代理
dao.save(user1);
int i = 100/0;
dao.save(user2);
}
测试类;
public void show() throws SQLException {
//加载配置文件,建立工厂
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//使用工厂对象获取对象
UserService service =(UserService) ac.getBean("userService");
//建立用户
User user1 = new User("熊大",1000);
User user2 = new User("熊二",2000);
//生成代理对象
Object proxyObj=JdkProxy.getProxy(service);
//强转成Service
UserService proxy = (UserService)proxyObj;
//使用代理对象进行保存数据
proxy.save(user1,user2);
}
jdk动态代理类事务管理: JdkProxy.java
写完以上代码,又发现一个问题,使用动态代理模式很是繁琐,若是还要给其余方法加强,好比:事务管理,日志的统计,日志的记录
因此就可使用SpringAOP这个技术,他能干什么: "能够直接对某个方法进行加强,不用你去繁琐的写方法加强"
还有一个问题:动态代理只能加强接口中的方法,没有接口,还不能加强...
增长方法,就不用本身写代理,能够对某些方法某些个方法进行加强
什么是AOP的技术?
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
AOP:面向切面编程.(思想--解决OOP遇到一些问题)
AOP采起横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
为何要学习AOP,能够在不修改源代码的前提下,对程序进行加强! !
总结一句话:AOP能够在不修改源代码的状况下,"对某些方法,某些程序进行加强"
优点:
AOP底层实现原理:
底层使用的就是代理技术:
为何Spring框架底层会提供多个代理技术呢?
Spring框架会自动作一个选择,当你的程序有没有接口,默认有:就会用JDK方式加强,若是不是JDK就会使用CGL方式加强
Joinpoint(链接点)所谓链接点是指那些被拦截到的点。在spring中,这些点指的是方法,由于spring只支持方法类型的链接点
Pointcut(切入点) -所谓切入点是指咱们要对哪些oinpoint进行拦截的定义
Advice(通知/加强)--所谓通知是指拦截到Joinpoint以后所要作的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Target(目标对象)--代理的目标对象
Weaving(织入)--是指把加强应用到目标对象来建立新的代理对象的过程
Proxy (代理) - -个类被AOP织入加强后,就产生一个结果代理类
Aspect(切面)--是切入点和通知的结合,之后我们本身来编写和配置的
作AOP开发,编写的内容
需求: 业务层对save方法进行加强(不改变代码对本来打印一条语句改成两条)
步骤:
导入新的坐标(jar包)
xxxxxxxxxx
<!-- AOP联盟 -->
aopalliance 版本1.0
<!-- aspectj -->
aspectjweaver 版本1.8.3
<!-- spring整合 -->
spring-aspects 版本5.0.2.ReLEASE
引入AOP的XML的名称空间
xxxxxxxxxx
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
编写新的类(切面类MyXMLAspect)
xxxxxxxxxx
/**
* 自定义切面类 = 切入点(表达式) + 通知组成(加强代码)
*/
public class MyXMlAspect{
//通知
public void log(){
//发送邮件/事务管理/记录日志
sout("加强方法执行了");
}
}
打开Spring核心配置文件applicationContext.xml
xxxxxxxxxx
<!--配置切面类,把该类交给IOC容器管理-->
<bean id="xmlAspect" class="...MyXmlAspect"></bean>
<!--配置AOP的加强-->
<aop:config>
<!--用上面的引用id配置切面 = 切入点 + 通知组成-->
<aop:aspect ref="xmlAspect">
<!--前置通知: UserServiceImpl的save方法执行前,会加强-->
<aop:before method="写切面类通知的方法" pointcut="切入点的表达式execution(public void cn.dong.service.UserServiceImpl.save())"/>
</aop:aspect>
</aop:config>
在配置切入点的时候,须要定义表达式,具体以下:
切入点表达式的格式以下:
execution[修饰符] 返回值类型 包名.类名.方法名(参数)
修饰符能够省略不写,不是必需要出现的
返回值类型是不能省略不写的,根据你的方法来编写返回值。可使用 “ * ” 替代。
包名例如:cn.dong.dao.UserDaoImpl
类名也可使用 * 号代替,也有相似的写法:*UserDaoImpl
方法也可使用 * 号代替
参数若是是一个可使用 * 号代替,若是想表明任意参数使用 ..
xxxxxxxxxx
<aop:before method="切面类方法" phintcut="切入点表达式"/>
<!--
切入点的表达式
execution(描述想对哪一个类的方法进行加强) 固定写法
[public] 修饰符(能够不写)
void返回值 () * (推荐使用* = 支持返回任意值)
包名+类名 * (所有类或包下的类都加强) 或者类名首位* = (*UserDao)以他开头
方法 * 也可使用*开头或者结尾
参数(int,String)或..(推荐使用) 表示任意类型的和个数的参数 = Object..
通用表达式:execution(public * cn.dong.*.*UserDao.*(..))
注意:加强方法首先找类,找不到不加强方法"并不会报错,包括返回值填错和参数"
-->
环绕通知: 目标方法执行先后,均可以进行加强,目标对象的方法须要手动执行
<aop:around method="..." pointcut="..." />
直接使用的问题: 目标对象的方法没有执行,须要手动执行目标对象的方法
在切面类的环绕通知方法增长参数,"ProceedingJoinPoint point"
point.procedd(); //让目标对象的方法去执行 (优势:这个方法写在这个位置,以前以后,能够去完成其余操做,这个方法自动会抛出异常,也能够利用异常均可以加强)
xxxxxxxxxx
总结: 前四个通知能够一块儿使用,环绕通知能够单独使用,就能够完成事务管理
配置文件+注解方式
搭建IOC环境...
搭建AOP环境步骤:
编写切面类,增长通知方法
@Compoent 把切面类交给IOC容器进行管理
@Aspect //声明切面类 == <aop:aspect ref="myXmlAspect">
在log通知方法上增长注解:切入点的表达式
开启AOP自动代理(在Spring核心配置文件配置)
开启AOP的自动代理 换成 配置类
xxxxxxxxxx
//配置类
value = "扫描下包名") // 扫描包 (
//开启自动代理 == (aop:aspectj-autoproxy) />
public class SpringConfig(){}
execution(public * cn.dong..UserDao.*(..))
建立一个配置类,增长如下注解
建立切面类增长如下注解:
若是不玩纯注解配置文件增长如下代码:
总结一句话:AOP能够在不修改源代码的状况下,"对某些方法,某些程序进行加强"
需求:查询数据库的所有用户信息,使用纯注解方式
xxxxxxxxxx
测试类;
value=SpringJUnit4ClassRunner.class)//运行单元测试 (
classes=SpringConfig.class)//扫描配置类 (
public class Test {
//自动装配到IOC容器管理
private UserService service;
junit.Test .
public void show(){
service.findAll();
}
}
xxxxxxxxxx
userServiceImpl实现类;
public class UserServiceImpl implements UserService {
/**
* 获取Dao持久层
*/
UserDaoImpl dao;
/**
* 查询所有用户信息
* @return
*/
public List<User> findAll() {
System.out.println(dao.findAll());
return dao.findAll();
}
}
xxxxxxxxxx
spring配置类;
//声明当前是一个配置类
value="cn.dong") //配置扫描的包 (
//开启自动代理
/**
* Spring配置类
*/
public class SpringConfig {
}
xxxxxxxxxx
Dao实现类;
/**
* 持久层
*/
public class UserDaoImpl implements UserDao {
/**
* 获取JdbcTemplate对象
*/
public JdbcTemplate template() {
//获取JdbcTemplate对象
JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
return template;
}
/**
* 查询全部用户
* @return
*/
public List<User> findAll() {
//写sql语句
String sql="select * from user ";
return template().query(sql, new BeanPropertyRowMapper<User>(User.class));
}
}
xxxxxxxxxx
切面类(加强方法);
/**
* 自定义切面类 并声明切面类将切面类交给IOC容器管理
*/
public class MyXmlAspect {
/**
*后置通知
*/
"execution(public * cn.dong.service.UserService.findAll(..))") (
public void log(){
System.out.println("查询了用户,关闭了程序");
}
}
什么模板技术: Spring框架中提供了不少持久层的模板类来简化编程,使用模板类写程序会变的简单
提供了Jdbc模板,Spring框架提供的
xxxxxxxxxx
<dependencies>
<!--经过maven窗体依赖,导入依赖的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--日志实现-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<!--日志接口-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--链接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<!--MySql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!--spring整合Junit测试jar包-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--AOP联盟-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--aspectj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!--spring整合-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--JDBC-->
<dependency>
<groupId>maven_repository.org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--事务-->
<dependency>
<groupId>maven_repository.org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
xxxxxxxxxx
使用new对象方式完成;
//建立链接池对象 Spring框架内置链接池对象
DriverManagerDataSource dataSource = new ...();
//设置四大参数 数据库 帐号 密码 包+类
...
//建立对象,提供模板
JdbcTempate template = new JdbcTemplate(链接池对象);
建立对象,提供模板
方法
xxxxxxxxxx
SpringIOC方式编写程序;
Spring配置文件配置链接池和模板;
<!--配置链接池-->
<bean id="dataSource" class="org.springframework.jdbc.datasource .DriverManagerDataSource>
<property name="driverClassName" value= "..." />
<property name= "url" value= "..." />
<property name= "username" value="..." />
<property name=" password" value="..." />
</bean>
<!--配置jdbc模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource" /></bean>
xxxxxxxxxx
测试方法;
SpringJunit4ClassRunner.class) (
value = "classpath:applicationContext.xml") (
private JdbcTemplate jdbcTemplate;
public void run1(){
....
}
配置开源的链接池,使用Druid开源的链接池,引入坐标 druid (德洛依)
druid属性配置文件: druid.properties
xxxxxxxxxx
第一种方式;
<!--引入外部属性文件-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfiggurer">
<property name="location" value="classpath:druid.properties"/>
</bean>
第二种方式;
<context:property-placeholder location="classpath:druid.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource .DriverManagerDataSource>
<property name="driverClassName" value= "${配置文件的key}" />
<property name= "url" value= "${...}" />
<property name= "username" value="${...}" />
<property name=" password" value="${...}" />
</bean>
修改
xxxxxxxxxx
public void show2(){
int i=template.update("update user set name = ?,money = ? where id = ?", "光头强", 10, 16);
System.out.println(i);
}
删除
xxxxxxxxxx
public void show3(){
template.update("delete from user where id = ?",16);
}
查询: 根据主键查询数据
xxxxxxxxxx
//queryForObject("sql语句",RowMapper<T>实体类封装查询数据 , Obect..数据)
User user = jdbcTemplate.queryForObject("select * from user where id=?",new BeanMapper());
//新建实体类,用来进行数据封装的 实现RowMapper<T要封装的类型>{
class BeanMapper implements RowMapper<Account>{
//建立user对象
User user = new User();
//封装数据
account.setId(resultSer.getInt("数据库对应的值id"));
account.setName(resultSer.getInt("数据库对应的值name"));
account.setMoney(resultSer.getInt("数据库对应的值money"));
reretn user;
}
}
查询:查询所有()
//queryForObject("sql语句",RowMapper<T>实体类封装查询数据 , Obect..数据)
User user = jdbcTemplate.queryForObject("select * from user",new BeanMapper());
xxxxxxxxxx
/**
* 根据主键查询用户信息
*/
public void show3(){
User user=template.queryForObject("select * from user where id = ?", new BeanPropertyRowMapper<User>(User.class),1);
System.out.println(user);
}
/**
* 查询所有用户信息
*/
public void show4(){
List<User> list=template.query("select * from user", new BeanPropertyRowMapper<User>(User.class));
System.out.println(list);
}
业务层: 转帐方法,三个参数: ont付款人 in收款人 money金额
持久层: 付款 收款 2个方法 各为2个参数
xxxxxxxxxx
测试类;
private UserService service;
public void show(){
service.pay(2,1,500);
}
业务层;
public class UserServiceImpl implements UserService {
//获取dao
private UserDao dao;
public void setDao(UserDao dao) {
this.dao=dao;
}
public void pay(int out, int in, int money) {
//收钱
dao.inMoney(in,money);
//给钱
dao.outMoney(out,money);
}
}
持久层;
/**
* 获取IOC容器中的JdbcTemplate对象
*/
private JdbcTemplate template;
public void setTemplate(JdbcTemplate template) {
this.template=template;
}
/**
* 发款
* @param out
* @param money
*/
public void outMoney(int out, int money) {
template.update("update user set money = money - ? where id = ?",money,out);
}
/**
* 收款
* @param in
* @param money
*/
public void inMoney(int in, int money) {
template.update("update user set money = money + ? where id = ?",money,in);
}
发现了一个问题 , 持久层的JdbcTemplate的模板 成员属性还有set方法,
每一次有个新的dao类,就得写一次jdbc模板和set方法,太繁琐,太麻烦 ,
这时候能够定义一个父类,把模板写到父类当中 ,
可是我们不用管,Spring框架已经帮忙写好了直接继承就好
xxxxxxxxxx
public void outMoney(int out, int money) {
this.getJdbcTemplate().update("update user set money = money - ? where id = ?",money,out);
}
xxxxxxxxxx
配置文件;
模板类能够不用配置
在配置dao时,直接依赖注入链接池
<bean id="userDao" class="cn.dong.dao.UserDaoImpl">
<property name="别名" ref="链接池引入id(别名)"/>
</bean>
范例:
xxxxxxxxxx
/**
* 收款
* @param in
* @param money
*/
public void inMoney(int in, int money) {
this.getJdbcTemplate().update("update user set money = money + ? where id = ?",money,in);
}
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:druid.properties"/>
<!--配置Druid链接池-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driverClassName}" />
<property name="url" value="${url}" />
<property name="username" value="root" />
<property name="password" value="${password}" />
</bean>
<!--配置实体类加载到IOC容器-->
<bean id="service" class="cn.dong.service.impl.UserServiceImpl">
<property name="dao" ref="dao"/>
</bean>
<bean id="dao" class="cn.dong.dao.impl.UserDaoImpl" >
<!--依赖注入链接池-->
<property name="dataSource" ref="dataSource" />
</bean>;
xxxxxxxxxx
1. PlatformTransactionManager接口 -- 平台事务管理器.(真正管理事务的类)。该接口有具体的实现类,根据不一样的持久层框架,须要选择不一样的实现类!
2. TransactionDefinition接口 -- 事务定义信息.(事务的隔离级别,传播行为,超时,只读)
3. TransactionStatus接口 -- 事务的状态
4. 总结:上述对象之间的关系:平台事务管理器真正管理事务对象.根据事务定义的信息TransactionDefinition 进行事务管理,在管理事务中产生一些状态.将状态记录到TransactionStatus中
5. PlatformTransactionManager接口中实现类和经常使用的方法
1. 接口的实现类
* 若是使用的Spring的JDBC模板或者MyBatis框架,须要选择DataSourceTransactionManager实现类
* 若是使用的是Hibernate的框架,须要选择HibernateTransactionManager实现类
2. 该接口的经常使用方法
* void commit(TransactionStatus status)
* TransactionStatus getTransaction(TransactionDefinition definition)
* void rollback(TransactionStatus status)
6. TransactionDefinition
1. 事务隔离级别的常量
* static int ISOLATION_DEFAULT -- 采用数据库的默认隔离级别
* static int ISOLATION_READ_UNCOMMITTED
* static int ISOLATION_READ_COMMITTED
* static int ISOLATION_REPEATABLE_READ
* static int ISOLATION_SERIALIZABLE
2. 事务的传播行为常量(不用设置,使用默认值)
* 先解释什么是事务的传播行为:解决的是业务层之间的方法调用!!
* PROPAGATION_REQUIRED(默认值) -- A中有事务,使用A中的事务.若是没有,B就会开启一个新的事务,将A包含进来.(保证A,B在同一个事务中),默认值!!
* PROPAGATION_SUPPORTS -- A中有事务,使用A中的事务.若是A中没有事务.那么B也不使用事务.
* PROPAGATION_MANDATORY -- A中有事务,使用A中的事务.若是A没有事务.抛出异常.
* PROPAGATION_REQUIRES_NEW(记)-- A中有事务,将A中的事务挂起.B建立一个新的事务.(保证A,B没有在一个事务中)
* PROPAGATION_NOT_SUPPORTED -- A中有事务,将A中的事务挂起.
* PROPAGATION_NEVER -- A中有事务,抛出异常.
* PROPAGATION_NESTED(记) -- 嵌套事务.当A执行以后,就会在这个位置设置一个保存点.若是B没有问题.执行经过.若是B出现异常,运行客户根据需求回滚(选择回滚到保存点或者是最初始状态)
TransactionDefinition接口事务的传播行为: 解决业务层方法相互调用,事务传播问题 (使用默认值)
步骤一: 配置平台事务管理器
xxxxxxxxxx
<!--配置平台事务管理器-->
<bean id="transactionMavager" class="DataSourceTransactionMavager">
<!--依赖注入链接池-->
...
</bean>
步骤二: 配置平台事务通知
xxxxxxxxxx
<!--引入tx名称空间-->
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
<!--配置平台事务通知(没有本身编写切面类,通知方法也是很多本身编写的,Spring框架提供的)-->
<tx:advice id="txAdvisor" transaction-manager="transactionManager">
<!--对方法进行加强,设置隔离级别,传播行为,超时的时间-->
<tx:mehod name="方法名" isolation="DEFAULT默认" propagation="REQUIRED默认" read-only="是不是只读的事务(查询的时候能够设置为只读) timeout="事务超时时间(-1永久)"/><tx:mehod name="方法名"/><!--能够配置多个--?
</tx:advice>
步骤三: 配置AOP的加强 (Spring框架提供的系统通知)
xxxxxxxxxx
<!--配置AOP的加强-->
<aop:config>
<!--Spring框架提供的系统通知,使用advisor标签-->
<aop:advisor advice-ref="txAdvice事务通知id" pointcut="execution(public * 包名+类名+方法名(..))" />
</aop:config>
范例:
xxxxxxxxxx
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:druid.properties"/>
<!--配置Druid链接池-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driverClassName}" />
<property name="url" value="${url}" />
<property name="username" value="root" />
<property name="password" value="${password}" />
</bean>
<!--配置实体类加载到IOC容器-->
<bean id="service" class="cn.dong.service.impl.UserServiceImpl">
<property name="dao" ref="dao"/>
</bean>
<bean id="dao" class="cn.dong.dao.impl.UserDaoImpl" >
<!--依赖注入链接池-->
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--依赖注入链接池-->
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置平台事务通知-->
<tx:advice id="txAdvisor" transaction-manager="transactionManager">
<!--对方法进行加强,设置隔离级别,传播行为,超时时间...-->
<tx:attributes>
<tx:method name="pay" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
</tx:attributes>
</tx:advice>
<!--配置AOP的加强-->
<aop:config>
<!--Spring框架提供的系统通知,使用advisor标签-->
<aop:advisor advice-ref="txAdvisor" pointcut="execution(public * cn.dong.service.impl.UserServiceImpl.pay(..))" />
</aop:config>
配置文件能够更改的地方有: 配置事务的通知 , 和配置AOP的加强(这些均可以省略不写使用注解代替)
步骤一: 开启注解的扫描
xxxxxxxxxx
在Spring配置开启注解的扫描
<context:component-scan base-package="xn.dong"/>
给持久层和业务层增长注解,把类交给IOC容器进行管理
在业务层 依赖注入dao 使用@Autowired注解
持久层依赖注入 jdbcTemplate (但在以前先在配置文件中将jdbcTemplate放到IOC容器管理)
开启事务对注解的支持
<tx:annotation-driven transaction-mavager="transactionManager" />
在Service增长注解@Transactional = 对Service的全部方法进行事务管理 也能够增长到方法中 对当前方法进行事务管理
@Transactional(isolation = Isolation.DEFAULT) //设置隔离级别 (能够点进注解查看属性)
范例:
xxxxxxxxxx
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:druid.properties"/>
<!--配置Druid链接池-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driverClassName}" />
<property name="url" value="${url}" />
<property name="username" value="root" />
<property name="password" value="${password}" />
</bean>
<!--注解扫描-->
<context:component-scan base-package="cn.dong" />
<!--加载到IOC容器管理-->
<bean id="template" class="org.springframework.jdbc.core.JdbcTemplate" >
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启事务对注解的支持-->
<tx:annotation-driven transaction-manager="transactionManager" />
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--依赖注入链接池-->
<property name="dataSource" ref="dataSource" />
</bean>
dao; ("dao")
private JdbcTemplate template;
service; ("service")
private UserDao dao;
xxxxxxxxxx
建立配置类,
扫描包
链接池
使用@Bean 注解 //把链接池保存到IOC容器中
模板对象
使用@Bean //把JdbcTemplate保存到IOC容器中
@Resource //不只能够做用在属性上,也能够做用在方法上
平台事务管理器
使用@Bean //把JdbcTemplate保存到IOC容器中
@Resource //不只能够做用在属性上,也能够做用在方法上
开启事务注解
@EnableTransactionManagement //开启事务注解
范例:
xxxxxxxxxx
配置类;
/**
* Spring配置类
*/
//声明配置类
value="cn.dong") //扫描包 (
public class SpringConfig {
/**
* 建立链接池对象
*/
"dataSource") (
public DataSource dataSource(){
//建立链接池对象,Spring框架内置了链接池
DriverManagerDataSource dataSource = new DriverManagerDataSource();
//设置四个参数
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/user?useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("accp");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return dataSource;
}
/**
* 建立Spring模板的JdbcTemplate对象
* @return
*/
name="dataSource") (
public JdbcTemplate jdbcTemplate(){
//建立JdbcTemplate对象
JdbcTemplate template=new JdbcTemplate();
//存放链接到JdbcTemplate
template.setDataSource(dataSource());
return template;
}
/**
* 建立平台事务管理对象
* @return
*/
name="dataSource") (
public PlatformTransactionManager createTransactionManager(){
DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource());
return manager;
}
}
"service") (
isolation=Isolation.DEFAULT) //对此类中全部方法都增长事务管理 (
public class UserServiceImpl implements UserService {
//获取dao
private UserDao dao;
public void pay(int out, int in, int money) {
//收钱
dao.inMoney(in,money);
int i = 100/0;
//给钱
dao.outMoney(out,money);
}
}
测试类;
SpringJUnit4ClassRunner.class) (
classes=SpringConfig.class) (
public class Demo {
private UserService service;
public void show(){
service.pay(2,1,100);
}
}