7、使用注解开发
Spring4之后,要使用注解开发,必须导入AOP包,使用注解,必须导入context约束,增加注解支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.atctbu.Dao"/>
<context:annotation-config/>
</beans>
7.1、属性如何注入
1、@Component
组件,放在类上说明这个类被Spring管理了,就是bean!
package com.atctbu.Dao;
import org.springframework.stereotype.Component;
//等价于<bean id="user" class="com.atctbu.Dao.User"/>
//@Component 组件
@Component
public class User {
public String name ="何锦涛";
}
//最后测试能输出name
2、@Value
@Component
public class User {
//相当于<property name="name" value="kuangshen"/>
@Value("何锦涛")
public String name ;
}
7.2、衍生的注解
@Component有几个衍生的注解,在我们web开发中,会按照mvc三层架构分层!
- Dao【@Repository】
- service 【@Service】
- controller 【@Controller】
这四个注解功能都是一样的,都是代表某类注册到Spring中,装配Bean
7.3、自动装配
@Autowaired(required = false)//如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空。
@Qualifier(value="dog222") 当使用@Autowired自动装配的环境比较复杂(有多个猫猫狗狗的时候),自动装配无法通过一个注解【@Autowired】完成的时候,使用@Qualifier(value="dog222"),去配合Autowired的使用,给它指定一个实现的值(bean对象)
@Nullable:字段标记了这个注解,说明这个字段可以为null;
@Resource:先通过名字查找,名字找不到根据类型查找,都找不到就报错,(有多个猫猫狗狗好像不行)@Resource(name = "cat2"),可以指定找哪个
7.4、作用域
1、@Scope(“singleton”)
配置作用域,和bean里面配置一样的
7.5、小结
xml与注解:
- xml更加万能,适用于任何场合!维护简单方便
- 注解不是自己类使用不了,维护相对复杂!
xml与注解最佳实践:
- xml用来管理bean;
- 注解只负责完成属性的注入;
- 我们在使用的过程中,只需要注意一个问题: 必须让注解生效,就需要开启注解的支持
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.atctbu.Dao"/>
<context:annotation-config/>
9、使用java的方式配置Spring
我们现在要完全不使用Spring的xml配置了,全权交给java来做!
JavaConfig是Spring的一个子项目,在Spring 4之后,它成为了核心功能
9.1、实体类User
package com.atctbu.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//说明这个类被Spring接管了,注册到了容器中
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("何锦涛07")//注入值
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
9.2、配置文件MyConfig
package com.atctbu.config;
import com.atctbu.poji.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;
//也会被Spring容器托管,注册到容器中,因为他本来就是一个Component,@Configuration代表这是一个配置类,和我们之前看的beans.xml是一样的
@Configuration
@ComponentScan("com.atctbu.pojo")
@Import(MyConfig2.class)
public class MyConfig {
//注册一个Bean,就相当于我们之前写的一个Bean标签,
//这个方法的名字,就相当于bean标签中的id属性
//这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User getUser(){
return new User();//就是返回值要注入的bean对象
}
}
9.3、MyConfig2
package com.atctbu.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
public class MyConfig2 {
}
9.4、测试类MyTest07
import com.atctbu.config.MyConfig;
import com.atctbu.poji.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest07 {
public static void main(String[] args) {
//如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig上下文来获取容器,通过类的class对象加载!
ApplicationContext Context = new AnnotationConfigApplicationContext(MyConfig.class);
// User getUser = Context.getBean("getUser", User.class);
User getUser = Context.getBean("user", User.class); //加了@ComponentScan后可以使用
System.out.println(getUser.getName());
}
}
这种纯java的配置方式,在SpringBoot中随处可见!
10、代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC:面试必问】
10.1、静态代理
角色分析:
● 抽象角色:一般会使用接口或者抽象类来解决
● 真实角色:被代理的角色
● 代理角色:代理真实角色,代理真实角色后,我们一-般会做-一些附属操作
● 客户:访问代理对象的人!
10.1.1、代码步骤
1.接口
package com.atctbu.demo01;
//租房接口
public interface Rent {
public void rent();
}
2.真实角色
package com.atctbu.demo01;
//房东
public class Host implements Rent{
public void rent() {
System.out.println("房东要出租房子!");
}
}
3.代理角色
package com.atctbu.demo01;
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
host.rent();
seeHouse();
hetong();
fare();
}
//看房
public void seeHouse(){
System.out.println("中介带你看房");
}
//合同
public void hetong(){
System.out.println("中介签合同");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
4.客户端访问代理角色
package com.atctbu.demo01;
public class Client {
public static void main(String[] args) {
//房东租房子
Host host = new Host();
//代理,中介帮房东租房子,但是呢,中介总有一些中间附属操作
Proxy proxy = new Proxy(host);
//你不用面对房东,直接找中介租房
proxy.rent();
}
}
10.1.2、代理模式(静态)的好处:
● 可以使真实角色的操作更加纯粹!不用去关注一 些公共的业务
● 公共也就就交给代理角色!实现了业务的分工!
● 公共业务发生扩展的时候,方便集中管理!
缺点:
● 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低
10.1.3、静态代理加深
1.接口
package com.atctbu.demo02;
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
2、真实角色
package com.atctbu.demo02;
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("修改了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
3、代理角色
package com.atctbu.demo02;
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
//日志方法
public void log(String msg){
System.out.println("使用了" + msg + "方法");
}
}
4、测试
package com.atctbu.demo02;
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
}
}
/*
使用了add方法
增加了一个用户
*/
10.2、动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的!
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口—JDK动态代理【我们在这里使用】
- 基于类: cglib
- java字节码实现: JAVAssist
需要了解两个类:Proxy:代理,InvocationHandler:调用处理程序
动态代理的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一 些公共的业务!
- 公共也就就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务!
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!
10.2.1、例子:
1、接口
package com.atctbu.demo03;
//租房接口
public interface Rent {
public void rent();
}
2、真实角色
package com.atctbu.demo03;
//房东
public class Host implements Rent {
public void rent() {
System.out.println("房东要出租房子!");
}
}
3、测试类
package com.atctbu.demo03;
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色:现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象
pih.setRent(host);
Rent proxy = (Rent)pih.getProxy(); //这里的proxy就是动态生成的,我们并没有去写它
proxy.rent();
}
}
4、代理类
package com.atctbu.demo03;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//等我们会用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
//固定的代码,死的,只需要改rent、getClassLoader()加载到类在哪个位置、
// getInterfaces(),表示它要代理的接口是哪个接口
//this、代表自己:InvocationHandler
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果,真正处理invoke负责执行真正要处理的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是使用反射机制实现!
seeHouse();
Object result = method.invoke(rent, args);
fare();
return result;
}
public void seeHouse(){
System.out.println("中介看房子");
}
public void fare(){
System.out.println("收中介费");
}
}
10.2.2、万能模板
1、万能模板
package com.atctbu.demo04;
import com.atctbu.demo03.Rent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//等我们会用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy(){
//固定的代码,死的,只需要改rent、getClassLoader()加载到类在哪个位置、
// getInterfaces(),表示它要代理的接口是哪个接口
//this、代表自己:InvocationHandler
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果,真正处理invoke负责执行真正要处理的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是使用反射机制实现!
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String msg){
System.out.println("执行了"+ msg + "方法");
}
}
2、测试类
package com.atctbu.demo04;
import com.atctbu.demo02.UserService;
import com.atctbu.demo02.UserServiceImpl;
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);//设置要代理的对象
//动态生成代理类
UserService proxy = (UserService) pih.getProxy();
proxy.add();
}
}
/*
执行了add方法
增加了一个用户
*/
11、AOP
11.1、什么是AOP
AOP (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一护的一种技术。AOP是0OP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
业务逻辑
验证参数 验证参数 验证参数 验证参数
前置日志 前置日志 前置日志 前置日志 代理对象
add() sub( ) mul( ) div( )
后置日志 后置日志 后置日志 后置日志
抽取横切关注点↓ AOP↑
验证 日志 业务逻辑
验证参数 前置日志 后置日志 add() div( )
sub() mul( ) 目标对象
切面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjNC7yrE-1617159142188)(C:\Users\何锦涛\AppData\Roaming\Typora\typora-user-images\image-20201117185153564.png)]
11.2、AOP在Spring中的作用
提供声明式事务;允许用户自定义切面
●横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
●切面(ASPECT) :横切关注点被模块化的特殊对象。即,它是一 个类。
●通知(Advice) :切面必须要完成的工作。即,它是类中的-一个方法。
●目标(Target) :被通知对象。
●代理(Proxy) :向目标对象应用通知之后创建的对象。
●切入点(PointCut) :切面通知执行的“地点”的定义。
●连接点(ointPoint) :与切入点匹配的执行点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-witIyfRi-1617159142190)(C:\Users\何锦涛\AppData\Roaming\Typora\typora-user-images\image-20201117185416473.png)]
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
通知类型 连接点 实现接口
前置通知 方法方法前 org. springframework aop.MethodBeforeAdvice
后置通知 方法后 org.springframework. aop AfterReturningAdvice
环绕通知 方法前后 org.aopalliance.interceptMethodInterceptor
异常抛出通知 方法抛出异常 org.springframework. aop.ThrowsAdvice
引介通知 类中增加新的方法属性 org-springframework.aop IntroductionInterceptor
即Aop在不改变原有代码的情况下,去增加新的功能.
11.3、使用Spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包!
<!-- https://mvnrepository. com/artifact/org. aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
11.3.1、方式一:使用Spring的API接口【主要是SpringAPI接口实现】
1、接口
package com.atctbu.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
2、实现类
package com.atctbu.service;
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户!");
}
public void delete() {
System.out.println("删除了一个用户!");
}
public void update() {
System.out.println("更新了一个用户!");
}
public void select() {
System.out.println("选择了一个用户!");
}
}
3、前置
package com.atctbu.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
//前置
public class Log implements MethodBeforeAdvice {
//method:要执行的目标对象的方法、objects/args:参数、target/o:目标对象
//这个方法会在执行方法前自动调用
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了");
}
}
4、后置
package com.atctbu.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
//后置
public class Afterlog implements AfterReturningAdvice {
//o/returnValue:返回值
public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了:" + method.getName() + "方法,返回结果为:" + returnValue);
}
}
5、测试
import com.atctbu.service.UserService;
import com.atctbu.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest09 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口,注意点
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
/*
com.atctbu.service.UserServiceImpl的add被执行了
增加了一个用户!
执行了:add方法,返回结果为:null
*/
6、applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.atctbu.service.UserServiceImpl"/>
<bean id="log" class="com.atctbu.log.Log"/>
<bean id="afterLog" class="com.atctbu.log.Afterlog"/>
<!--配置AOP,alt+enter,需要导入AOP的约束-->
<aop:config>
<!--切入点:expression:表达式、execution(要执行的位置!*******)、*代表任意的类-->
<!--.*(..):.*代表所有的方法:..代表可以有任意的参数-->
<!--execution表达式:可百度-->
<aop:pointcut id="pointcut" expression="execution(* com.atctbu.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加!-->
<!--把log这个类切入到上面这个方法上面-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
11.3.2、方式二:自定义类实现AOP【主要是切面定义】
1、applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.atctbu.service.UserServiceImpl"/>
<bean id="log" class="com.atctbu.log.Log"/>
<bean id="afterLog" class="com.atctbu.log.Afterlog"/>
<!--配置AOP,alt+enter,需要导入AOP的约束-->
<!-- <aop:config>-->
<!-- <!–切入点:expression:表达式、execution(要执行的位置!*******)、*代表任意的类–>-->
<!-- <!–.*(..):.*代表所有的方法:..代表可以有任意的参数–>-->
<!-- <aop:pointcut id="pointcut" expression="execution(* com.atctbu.service.UserServiceImpl.*(..))"/>-->
<!-- <!–执行环绕增加!–>-->
<!-- <!–把log这个类切入到上面这个方法上面–>-->
<!-- <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>-->
<!-- <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>-->
<!-- </aop:config>-->
<!--方式二:自定义类-->
<bean id="diy" class="com.atctbu.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面,ref:要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.atctbu.service.UserServiceImpl.*(..))"/>
<!--通知:before要执行什么方法-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
2、DiyPointCut
package com.atctbu.diy;
public class DiyPointCut {
public void before(){
System.out.println("==========方法执行前==========");
}
public void after(){
System.out.println("==========方法执行后==========");
}
}
3、测试类
import com.atctbu.service.UserService;
import com.atctbu.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest09 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口,注意点
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
/*
==========方法执行前==========
增加了一个用户!
==========方法执行后==========
*/
11.3.3、方式三:使用注解方式实现AOP
@Aspect //标志这个类是一个切面
1、AnnotationPointCut
package com.atctbu.diy;
//方式三:使用注解方式实现AOP
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect //标志这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.atctbu.service.UserServiceImpl.*(..))")//切入点
public void before(){
System.out.println("=====方法执行前=====");
}
@After("execution(* com.atctbu.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====方法执行后=====");
}
//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点:
@Around("execution(* com.atctbu.service.UserServiceImpl.*(..))")
//JoinPoint:与切入点匹配的执行点
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("=====方法环绕前=====");
Signature signature = jp.getSignature();//获得签名
System.out.println("signature:" + signature);
//执行方法
Object proceed = jp.proceed();
System.out.println("=====方法环绕后=====");
System.out.println(proceed);
}
}
2、测试类
import com.atctbu.service.UserService;
import com.atctbu.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest09 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口,注意点
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
3、applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.atctbu.service.UserServiceImpl"/>
<bean id="log" class="com.atctbu.log.Log"/>
<bean id="afterLog" class="com.atctbu.log.Afterlog"/>
<!--方式三-->
<!--注册bean,也可以通过Component-->
<bean id="annotationPointCut" class="com.atctbu.diy.AnnotationPointCut"/>
<!--开启注解支持! JDK(默认:proxy-target-class="false") cglib(proxy-target-class="true")-->
<aop:aspectj-autoproxy/>
</beans>
12、整合Mybatis
步骤:
-
导入相关jar包
- junit
- mybatis
- mysql数据库
- spring相关的
- aop织入
- mybatis-spring【new】
<dependencies> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!--junit驱动--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!--Spring操作数据库的话,还需要一个Spring-JBDC--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.1</version> </dependency> <!--织入必须导这个--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.3</version> </dependency> </dependencies>
-
编写配置文件
-
测试
12.1、回忆Mybatis
1、编写实体类
2、编写核心配置文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username=root
password=q1314159
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="q1314159"/>
</properties>
<!--配置别名-->
<typeAliases>
<package name="com.atctbu.hjt.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.atctbu.hjt.dao.UserMapper"/>
</mappers>
</configuration>
3、编写接口
4、编写Mapper.xml
<mapper namespace="com.atctbu.hjt.dao.UserMapper">
<select id="selectUser" resultType="User">
select * from mybatis.user
</select>
</mapper>
5、测试
public void selectUser() throws IOException {
String resources = "mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
for (User user:userList
) {
System.out.println(user);
}
}
User{id=1, name='何锦涛', pwd='123456'}
User{id=2, name='严瑶', pwd='123456'}
User{id=3, name='王大拿', pwd='12121212'}
12.2、mybatis-spring
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession
并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException
。 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory
和至少一个数据映射器类。
在 MyBatis-Spring 中,可使用 SqlSessionFactoryBean
来创建 SqlSessionFactory
。 要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
12.2.1、具体操作
-
编写数据源配置
spring-dao.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid 我们这里使用Spring提供的JBDC:"org.springframework.jdbc.datasource.DriverManagerDataSource ↑:这个东西是导入的spring-jbdc依赖 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="q1314159"/> </bean> <!--SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!--绑定Mybatis配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/atctbu/hjt/dao/*.xml"/> </bean> <!--sqlSession:官方建议sqlSessionTemplate,这就是我们使用的sqLSession --> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--只能使用构造器注入sqlSessionFactory,因为它没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> <!----> <!-- <bean id="userMapper" class="com.atctbu.hjt.dao.UserMapperImpl">--> <!-- <property name="sqlSession" ref="sqlSession"/>--> <!-- </bean>--> </beans>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--配置别名--> <typeAliases> <package name="com.atctbu.hjt.pojo"/> </typeAliases> <!--设置--> </configuration>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <import resource="spring-dao.xml"/> <!----> <bean id="userMapper" class="com.atctbu.hjt.dao.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> </beans>
-
SqlSessionFactory
-
SqlSessionTemplate
-
需要给接口加实现类【】
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.atctbu.hjt.dao.UserMapper"> <select id="selectUser" resultType="User"> select * from mybatis.user </select> </mapper>
package com.atctbu.hjt.dao; import com.atctbu.hjt.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper{ //原来我们的所有操作都使用SqlSession来执行,现在都使用SqlSessionTemplate; private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } public List<User> selectUser() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); } }
-
将写的实现类注入到Spring中
-
测试
@Test
public void selectUser() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
List<User> userList = userMapper.selectUser();
for (User user: userList
) {
System.out.println(user);
}
}
User{id=1, name='何锦涛', pwd='123456'}
User{id=2, name='严瑶', pwd='123456'}
User{id=3, name='王大拿', pwd='12121212'}
12.3、整合Mybatis方式二:SqlSessionDaoSupport
SqlSessionDaoSupport
是一个抽象的支持类,用来为你提供 SqlSession
。调用 getSqlSession()
方法你会得到一个 SqlSessionTemplate
,之后可以用于执行 SQL 方法,就像下面这样:
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
public User getUser(String userId) {
return getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
}
}
在这个类里面,通常更倾向于使用 MapperFactoryBean
,因为它不需要额外的代码。但是,如果你需要在 DAO 中做其它非 MyBatis 的工作或需要一个非抽象的实现类,那么这个类就很有用了。
SqlSessionDaoSupport
需要通过属性设置一个 sqlSessionFactory
或 SqlSessionTemplate
。如果两个属性都被设置了,那么 SqlSessionFactory
将被忽略。
假设类 UserMapperImpl
是 SqlSessionDaoSupport
的子类,可以编写如下的 Spring 配置来执行设置:
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
12.3.1、具体操作
UserMapperImpl2
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
public List<User> selectUser() {
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
applicationContext.xml
<bean id="userMapper2" class="com.atctbu.hjt.dao.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
测试UserMapperImpl2
@Test
public void selectUser() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
List<User> userList = userMapper.selectUser();
for (User user: userList
) {
System.out.println(user);
}
}
//也能查出来结果
13、声明式事务
13.1、回顾事务
- 要么都成功,要么都失败
- 事务在项目开发中,十分重要。涉及数据一致性问题
- 确保完整性和一致性
事务的ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器或数据库中!
将sql的删除语句故意写错
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
public List<User> selectUser() {
User user = new User(4,"小何","123456");
UserMapper mapper1 = getSqlSession().getMapper(UserMapper.class);
mapper1.addUser(user);
mapper1.deleteUseer(4);
return mapper1.selectUser();
}
public int addUser(User user) {
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.addUser(user);
}
public int deleteUseer(int id) {
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.deleteUseer(id);
}
}
这个时候我们测试
public void Test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper2 = context.getBean("userMapper2", UserMapper.class);
List<User> userList = userMapper2.selectUser();
for (User user:userList
) {
System.out.println(user);
}
}
仍然插入成功了,我们并不希望插入成功
13.2、spring中的事务管理
- 声明式事务:AOP
- 编程式事务:需要在代码中进行事务的管理
思考:
为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况
- 如果不在Spring中取配置声明式事务,我们就需要在代码中手动配置事务!
- 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题
13.2.1、标准配置(结合AOP织入事务)
要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager
对象:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
我们在不改动原来的代码情况下配置事务的织入
例子:
spring-dao.xml中
<!--结合AOP实现事务的织入-->
<!--配置事务通知:-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性:new propagation-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<!--dao下的所有类所有方法都可以使用这个事务-->
<aop:pointcut id="txPoingCut" expression="execution(* com.atctbu.hjt.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoingCut"/>
</aop:config>
再次运行错误的测试(删除错误)
//UserMapperImpl
public List<User> selectUser() {
User user = new User(4,"小何","123456");
UserMapper mapper1 = getSqlSession().getMapper(UserMapper.class);
mapper1.addUser(user);
mapper1.deleteUseer(4);
return mapper1.selectUser();
}
@Test
public void Test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper2 = context.getBean("userMapper2", UserMapper.class);
List<User> userList = userMapper2.selectUser();
for (User user:userList
) {
System.out.println(user);
}
}
这时,执行失败,但是插入也没有成功
13.2.2、事务的传播:面试会考
**1、PROPAGATION_REQUIRED:**如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
**2、PROPAGATION_SUPPORTS:**支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘
**3、PROPAGATION_MANDATORY:**支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
**4、PROPAGATION_REQUIRES_NEW:**创建新事务,无论当前存不存在事务,都创建新事务。
**5、PROPAGATION_NOT_SUPPORTED:**以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
**6、PROPAGATION_NEVER:**以非事务方式执行,如果当前存在事务,则抛出异常。
**7、PROPAGATION_NESTED:**如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。