Spring应用与知识点
Spring组成
bean
BeanFactory管理bean
我们自己实现一个静态beanfactory,读取bean文件,实例化bean
public class BeanFactory { private static Properties props; private static Map<String, Object> beans; static {//静态,跟随类加载初始化 try { props = new Properties(); InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");//硬编码 • props.load(in); • beans = new HashMap<>();//初始化 • } catch (Exception e) { • throw new ExceptionInInitializerError("Cannot find bean.properties"); • } } public static Object getBean(String beanName) { Object bean = null; try { String beanPath = props.getProperty(beanName);//得到bean类路径 bean = Class.forName(beanPath).getDeclaredConstructor().newInstance();//使用反射 实例化 } catch (Exception e) { e.printStackTrace(); } • return bean; } public static Object getSingletonBean(String beanName) {//单例,懒加载 Object bean = null; if(beanName != null) {//输入不为空 if(beans.containsKey(beanName)) {//已存在,实例化过(有缓存), bean = beans.get(beanName); } else { bean = getBean(beanName);//直接获取 beans.put(beanName, bean);//没有,(缓存) } } • return bean; } public static IEarPhone getAirPod() {//供xml中方法三使用 return new AirPod(); }
properties文件:
等号左边beanname , 右边是properties,也叫beanPath
使用
上图:this.airpod=xx.getbean("xxx"),这样就不用依赖new实例了由Spring容器实例化。
背景介绍:上图Phone类有个实例字段是耳机类的引用,要解决new AirPod的高耦合问题。
第四种方法无需再 使用new airpod:
正式使用Spring
教材内容:Spring中无论使用那种容器都需要从配置文件中读取JavaBean信息,然后根据信息创建JavaBean实例对象并注入其依赖的属性。要在Spring Ioc容器中获取一个bean,首先要在配置文件中配置bean标签,Spring控制反转机制会根据配置,实例化这个bean实例。
BeanFactory采用了工厂模式,通过从xml配置文件或属性文件中读取JavaBean的定义来创建、配置和管理JavaBean。BeanFactory有很多实现类,其中XmlBeanFactory可以通过流行的XML文件读取配置来加载JavaBean。
使用前获取jar包。
BeanFactory实现了IoC(控制反转)控制。所以可以成为IOC容器, ApplicationContext扩展了BeanFactory容器并添加了国际化和生命周期时间的发布监听等更强大的功能。 6.1 ApplicationContext的特点:1. 默认单例 2. 及时加载,是在load xml文件的时候进行的实例化,bean.xml 文件中读一个就初始化一个
6.2 ApplicationContext接口的三个实现类 ClassPathXmlApplicationContext: 用于读取resources下的XML文件 FileSystemXmlApplicationContext: 可以读取任意位置的XML文件,需要完整的path(绝对路径) AnnotationConfigApplicationContext: 用于读取Annotation 6.3 ApplicationContext:Spring 容器, 容器创造Bean的三种方法 直接创造Bean; 通过工厂类创造; 通过静态工厂 6.4 Bean的作用范围 scope:
singleton 单例 prototype 多例 request 在web的环境下,在请求范围内是单例,跨请求多例 session 在web的环境下,在session的范围内是单例,跨session是多例 global-session 在集群环境下,在global session的范围内是单例,跨global session是多例 在非集群环境下,它等同于session 6.5 Bean生命周期 Singleton模式中: Bean初始化:及时加载,在load XML的时候进行实例化 Bean的存在周期:只要容器存在,Bean就一直存在 Bean的销毁:容器销毁的时候,Bean销毁 Prototype模式中: Bean初始化:懒加载,当要调用getBean的时候进行实例化 Bean的存在周期:由用户代码决定 Bean的销毁:通过JVM的GC来释放,当没有变量指向Bean以后,JVM会在适当的时候GC Bean
下图是xml中方法二的工厂,但是仍需要new airpod,所以一般不用
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); • IEarPhone earPhone5 = (IEarPhone) ac.getBean("airPod"); • System.out.println(earPhone5); • IEarPhone earPhone6 = (IEarPhone) ac.getBean("airPod"); • System.out.println(earPhone6);//相同,单例。读文件时加载(实例化)完了,后get时仅仅是返回。多例,不同 • ac.close();//容器关闭时,会调用destory
引入:除引入外,还有library
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.13.RELEASE</version> </dependency>
补充毕设项目:
public class SpringBeanFactory { public static Object getBean(String[] paths, String name){ XmlWebApplicationContext ctx = new XmlWebApplicationContext(); ctx.setConfigLocations(paths); ctx.setServletContext(new MockServletContext("")); ctx.refresh(); return ctx.getBean(name); } public static Object getBean(String name){ String[] paths = { "applicationContext.xml" };//注解文件 return getBean(paths,name); } }
做测试用:
public class CRUDTest extends TestCase { Logger log = Logger.getLogger(CRUDTest.class); /** * 测试创建 */ public void testCreate() { IAuthUserService authUserService = (IAuthUserService) SpringBeanFactory.getBean("authUserServiceImpl"); log.info("------------------ create start "); AuthUser authUser = new AuthUser(); authUser.setUsername("test0001"); authUser.setPassword("test0001"); authUser.setRealname("用户0001"); authUserService.createSelectivity(authUser); System.out.println("id = " + authUser.getId()); log.info("------------------ create end "); }
依赖注入:Dependency Injection
一个类要使用到另一个类的实例时,传统程序通常由调用者来创建被调用者的实例。但在Spring里创建被调用者的工作不再由调用者完成,因此成为控制反转。创建被调用者实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。
Spring在解耦合以后,通过设置完成复杂类实例化。
上例phone的成员变量是耳机类,只需要再bean.xml中装配这个耳机类即可。
而本例A类是一个复杂类,要实例化A这个复杂类(有不同类型的实例字段,需要赋值)
第一种方式构造方法注入
name(get属性去掉get并且首字母变小写后的实例字段)/index(序号,从0开始)/type(类型,按顺序) + value/ref 可以不用ref属性,嵌入bean标签; 可以不用value属性,用value标签
<bean id="a" class="cn.liwang.A"> <constructor-arg index="1" value="5"></constructor-arg> <constructor-arg name="i"> <value>5</value> </constructor-arg> <constructor-arg name="str" value="abc"></constructor-arg> <constructor-arg name="b" ref="b"></constructor-arg> <!-- ref内容为已有的beanname--> <constructor-arg name="b"> <bean class="cn.liwang.B"></bean> </constructor-arg> <constructor-arg name="c" ref="c"></constructor-arg> <constructor-arg type="java.lang.Integer" value="6"></constructor-arg> </bean>
set方法 注入
教材:简单直观,最常用,设置注入指通过setter方法传入被调用者的实例。value标签为name属性赋值,Spring会把这个标签提供的属性值注入到指定的JavaBean中。
基础类型 + Bean + 集合类型(数组+List+Set\Map+Property)<基础类型,Bean> name(实例字段) + value/ref 可以不用ref,嵌入bean标签; 可以不用value,嵌入value标签 构造方法注入和set方法注入的区别:构造的必须和构造方法的参数表一致,set可以不一致 数组,List,Set : array,list,set这三种标签可混用
注意上图47行不,A类中有ib,为了注释练习,有改动
下图为A类,只看成员变量,先忽略注解
A的使用:
// ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// A a = (A) ac.getBean("a");这一句是精髓,这样获得而不是直接new A.成员变量赋值也在bean中注入,而不是a.setxxx =xxx
而为什么这么用?
类有改动只需要修改配置文件,用new的方式还需要修改无限的java代码。因为xml文件一个bean可以被复用。
Annotation注解
Annotation 实现IoC/DI 只能用与基础数据类型和bean类型,集合的注入只能用配置文件
// XML中有: <bean id="" class="" scope="" init-method="" destory-method=""> // <property name="" value=""|ref=""></property> // </bean> 用来与annotation作对比
1.使用注解+xml
8.1 创建对象(实例) xml怎么写: bean(id, class) 与下面作对比 annotation怎么写:
@Component(value="<id>") 如果id是类名的小驼峰写法,那么可以不用写value 如果id不是类名的小驼峰模式,需要写,而且ac.getBean那里要一致 以下三个和Component没有任何区别 @Controller 一般用于表示层/Web层 @Service 一般用于业务层/中间件层/service层 @Repository 一般用于持久层/数据层
8.2 依赖注入注解
xml中:property(name, value) 基础数据
annotation: 1@Value(value="<值>") 2通过@PropertySource({"classpath:<properties文件的路径>"})+@Value(value="${key}") 3还可以:SpEL, Spring 定义的表达式语言 (有点像JS) #{SpEL表达式} xml中:property(name, ref) bean annotation: 1@Autowired,如果刚好有且只有一个类型相符的bean,那么这个bean就可以被注入; 如果一个都找不到,报错, 怎样才能找到?需要 在其同类型的类代码中添加@component注释。见下图B类
2.@Autowired, @Qualifier("<id>") 如果有两个或者两个以上类型相符的bean,那么要看bean的id和当前属性是否有匹配的。@Qualifier必须配合@Autowired使用如果有且仅有一个匹配的,那么成功,如果没有或者有多个,报错 Qualifier不能单独使用 3.@Autowired+@Qualifier("<id>") 等价于 @Resource(name="<id>") 要引入dependency包,import module1.3.2 pom文件addlibrary;
8.3 作用范围 xml:bean(scope), singleton, prototype annotation: @Scope("singleton|prototype")
在注入时的bean里也可以用,见下图B2类
8.4 生命周期 xml: bean(init-method, destory-method) annotation: @PostConstruct, @PreDestory
分别是bean初始化后与销毁前会被调用的方法。
注意A.java使用了@propertysource,注册了properties文件。
这是B类,
这是B2,与B类型相同,不会自动匹配,需要区分
bean类里也可以加作用范围标签
context:component-scanscan scan scan component scan comtext domponentscan
使用: ApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml");
2配置类
在《Spring实战》中介绍注解直接介绍此方式,并直言,配置是一种过时方式,很难想象在没有SpringBoot的情况下开发Spring应用。
8.5 配置类,spring中的新注解 完全替换*.xml annotation: @Configuration 替换xml, @ComponentScan 告知spring在创建容器的时候要扫描包 8.6 问题:
(1)如果需要一个类产生多个实例,比如A(加入想实例化多个A的对象,xml中能罗列多个bean标签,只要ID名不同,class相同,但是注解只能有一个component(实例)); (2)需要实例化的类不是自己写的,jar包中的类无法改动,不能添加注解 解决:annotation: @Bean 实例化,并且把实例放入Spring容器
@Bean 表明该方法返回的对象,会以bean形式添加到
上图的set方法会被@value注解覆盖,原因看下面工作模式总结,方法赋值在前,set注入在后
为什么这里又用 new字段呢?因为configuration相当于xml ,n这里的@bean相当于xml中的bean标签,都到这一步了耦合是没有问题的,就是要解耦合后在xml注入(与业务类解耦,在配置类中注入,是讲得通的。)
注意:在这里@Bean(name = "a2"),属于下面总结的工作模式3,A类里的注释@value等注释对a2仍有效!
8.7工作模式:1 xml + 类中有@Component等 2 @Configuration + 类中有@Component等 3 @Configuration, @Bean + 类中有@Component等 如果: @Component + @Value: 过程是首先根据类的缺省构造方法生成A,然后使用set(@Value)注入 如果: @Bean + @Value(如a2): 过程是调用@Bean修饰的方法获得A,然后使用set注入。 可以在@Bean修饰的方法中注入值,但是会被@Value覆盖 8.8 @Configuration什么时候不可以不写? 8.8.1, 如果配置类被当做参数传给AnnotationConfigApplicationContext构造方法,那么@Configuration可以没有 8.8.2, 如果配置类没有传给AnnotationConfigApplicationContext构造方法: 1. 必须有@Configuration 2. @Import,在已经引入的配置类中使用
上图这样SpringConfiguration2中的@configuration可以注释掉
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class, SpringConfiguration2.class);
这样参数传进去了也可以注释掉
8.9 @Qualifier在参数中的应用,在配置类的参数中应用,当同参数类型有多个可使用实例时指明是使用Spring容器中的哪个Bean实例
与上面注解成员变量时意义差不多,都是有多个相同类型的实例可用的情况下,做区分。
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);这里配置类2中的标签要写· A a = (A) ac.getBean("a3"); System.out.println(a); A a2 = (A) ac.getBean("a3"); System.out.println(a2); System.out.println(a == a2);//单例True,反之F
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.13.RELEASE</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
import xml
Spring xml文件中 import resource 是一种指令,用于导入其他的Spring配置文件,从而实现配置文件的分模块和复用。
你可以将数据库配置,事务管理,AOP切面等分别放在不同的配置文件中,然后在applicationContext.xml中使用如下代码导入。
防止单个文件太臃肿,类似.js文件的引入
毕设项目 对shiro.xml 的引用:
<!-- 引入shiro --> <import resource="shiro.xml" />
Spring AOP
背景:
-
有一个类Process,Process有三个动作,现在有需求:在步骤2前后加点什么,于是写出了Process2类,复制了Process的代码,添加了点东西,这时候又来需求...又写新建Process3,代码复用、重复工作严重。
-
OOP的抽象,Process2、Process3 继承自 Process
-
静态代理: 不用继承,用代理,AbstractProcess作为父类,Process,Process2,Process3都从之继承,Process2, Process3重写 action1, action2, action3,但是干活的时候使用Process,Process2和Process3是代理类,Process真正干活。
-
静态代理还要将上面及几个类写好,现在基于反射实现动态代理,能即时创造一个代理类。 静态代理:在编写代码的时候就已经定义好,称为静态代理 动态代理:代理类的生成是在运行时临时的产生,比如使用反射,称为动态代理 这时候Process2,Process3就要退场了(要使用Process2、Process3InvocationHandler来生成动态代理类)。InvocationHandler必须要给接口,这时候创建接口IProcess.将抽象类中的方法复制到IProcess中并在接口中去掉这些方法的abstract修饰符,让抽象类实现这个接口
首先有这些步骤或动作,已经被重构到接口中了:IProcess
public interface IProcess { public void action1(); public void action2() throws Exception ; public void action3(); public int action4(); public void action5(int i); public void action6(String i); }
抽象类AbstractProcess继承了IProcess
public abstract class AbstractProcess implements IProcess { private IProcess process;//代理模式 public IProcess getProcess() { return process; } public void setProcess(IProcess process) { this.process = process; } public static void doActions(IProcess process) throws Exception { process.action1(); process.action2(); process.action3(); process.action4(); process.action5(1); process.action6("a"); } }
Process类
import org.springframework.stereotype.Component; @Component("process") public class Process extends AbstractProcess { // 步骤1 @Override public void action1() { System.out.println("action 1"); } // 步骤2 @Override public void action2() throws Exception { System.out.println("action 2"); System.out.println("action 2 again"); // throw new Exception("abc"); } // 步骤3 @Override public void action3() { System.out.println("action 3"); } @Override public int action4() { System.out.println("action 4"); return 1; } @Override public void action5(int i) { System.out.println("action 5"); } @Override public void action6(String i) { System.out.println("action 6"); } }
Process2类看看即可:
package cn.liwang;
//public class Process2 extends Process {//OOP继承 public class Process2 extends AbstractProcess { // public void doActions() {//演示最初的代码复用 // // 步骤1 // System.out.println("action 1"); // // 步骤2 // System.out.println("before action 2"); // System.out.println("action 2"); // System.out.println("action 2 again"); // System.out.println("after action 2"); // // 步骤3 // } // System.out.println("action 3");
// @Override // public void action2() {//演示OOP继承时的代码,复习时不用看 // System.out.println("before action 2"); // super.action2(); // System.out.println("after action 2"); // }
@Override public void action1() { this.getProcess().action1(); } @Override public void action2() throws Exception { System.out.println("before action 2"); this.getProcess().action2(); System.out.println("after action 2"); } @Override public void action3() { this.getProcess().action3(); } @Override public int action4() { System.out.println("action 4"); return 1; } @Override public void action5(int i) { System.out.println("action 5"); } @Override public void action6(String i) { System.out.println("action 6"); } }
Process2InvocationHandler类,看看即可
package cn.liwang; //process2,3是代理类,干活的类是process import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class Process2InvocationHandler implements InvocationHandler { private IProcess process;
public IProcess getProcess() { return process; } public void setProcess(IProcess process) { this.process = process; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object returnValue = null; // 通过调用的名称来判断 if("action2".equals(method.getName())){//action2前后要加这些东西 System.out.println("before action 2"); returnValue = method.invoke(this.process, args); System.out.println("after action 2"); } else { returnValue = method.invoke(this.process, args); } return returnValue; } }
实现:
// Process2InvocationHandler handler = new Process2InvocationHandler(); // handler.setProcess(process); // IProcess process2 = (IProcess) Proxy.newProxyInstance(process.getClass().getClassLoader(), // AbstractProcess.class.getInterfaces(), // handler); // // AbstractProcess.doActions(process2); //为什么这样写,因为直接写process2.doactions时由于真正干活的类是process,action2输出: System.out.println("action 2"); System.out.println("action 2 again"); //只有改写AbstractProcess.doActions,让它接收参数,才会输出process2中的action2: // Process3InvocationHandler handler = new Process3InvocationHandler(); // handler.setProcess(process); // // IProcess process3 = (IProcess) Proxy.newProxyInstance(process.getClass().getClassLoader(), // AbstractProcess.class.getInterfaces(), // handler); // // AbstractProcess.doActions(process3);
Spring AOP
JOINPoint(连接点):比如每个action,就是可切分开的动作。连接点是程序执行过程中插入切面的地点,它实际上是对象的一个操作。
PointCut(切入点):比如process2中action2,在他前后加点东西。切入点是切面注入程序中的位置,切面是通过切入点被注入的。
通知Advice:切面的某个特定的连接点上执行的动作。
Target Object:目标对象,是被通知的对象,既可以是编写的类,也可以是需要添加指定行为的第三方类,AOP会注意目标对象的变动,并随时准备向目标对象注入切面。
AOP代理(AOP ):将通知应用到目标对象后创建的对象。
织入:把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象。
Logger类里面含有注解:
package cn.liwang; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component("logger")// @Aspect **//@Aspect表示切面类,一把刀(提供通知方法)本例中毫无疑问是logger类** public class Logger { public void printLog() { System.out.println("Logger say xxxx here."); } public void printLog2() { System.out.println("Logger say xxxx2 here."); } @Around("action2()") public Object processAdvice(ProceedingJoinPoint pjp) {//Processadvice方法,参数就是xml中要劫持的方法比如action2. Proceedingjointpoint,要用它是必须要用抛出异常的。有了它,就可以手动设置可以出现哪些通知,比如try中可以安排后置,catch中可以安排异常通知等等。 // System.out.println("Logger around here."); System.out.println("Logger before action"); Object rtValue = null; try { Object[] args = pjp.getArgs(); rtValue = pjp.proceed(args); System.out.println("Logger after-returning action"); } catch (Throwable t) { System.out.println("Logger after-throwing action"); } finally { System.out.println("Logger after action"); } return rtValue; }
从这里开始:是为了演示注解后加上的:
@Pointcut("execution(void cn.liwang.Process.action2())") 只要想劫持某个方法,就得现写进来这里把process中的action2()搬过来了,只需要方法名,无需方法内容,注解要写。 private void action2(){}
// @Before("action2()") public void printBefore() { System.out.println("Logger say Before here."); } // @AfterReturning("action2()") public void printAfterReturning() { System.out.println("Logger say after returning here."); } // @AfterThrowing("action2()") public void printAfterThrowing() { System.out.println("Logger say after throwing here."); } // @After("action2()") public void printAfter() { System.out.println("Logger say after here."); } }
下面使用SpringAop实现:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.13.RELEASE</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency>
方式一:xml
创建Spring的xml文件,文件头照例去复制。
然后将干活的类与切面类装配进去。
然后使用标签进行AOP配置:
详解上图如何配置:
aop:pointcut标签即切入点是谁:标签有两个属性:id随便,expression:
="execution(方法表达式)" <方法表达式>: public void cn.liwang.Process.action2() 访问修饰符可以省略:void cn.liwang.Process.action2() 返回值可以变成 * : * cn.liwang.Process.action2() 方法名可以是通配符 *: void cn.liwang.Process.*() 包名可以是通配符 *: 可以是void cn.liwang.*.*() 也可以:void *.*.*.*()有几个包写几个* 如果不确定包的级别 *: void *..*() 就这样写,123都可以被运行,int类型就不行了 我们还可以设定函数的参数: 基础类型:int 类名: java.lang.String 写全 适配任意参数,有或者没有: .. 一般的写法,某一个类里所有的方法:* cn.liwang.Process.*(..) 方法任意,参数不限
aop:aspect标签,切面类:id随便命名,ref属性是提供advice的类,然后该标签内嵌入如aop:around通知类型标签:
method属性为切面类里的通知方法,pointcut-ref属性内容为aop:pointcut标签的 id属性
通知类型:
Advice类型 before 前置通知 after-returning 后置通知 after-throwing 异常通知,与后置通知只能运行一个 after 最终通知 around 环绕通知,本例只输出了logger advice,输出时没有输出action2方法,只出现了通知。 PointCut需要你自己手动调用,所有的其他四种通知都能但需要手动安排
使用:
ApplicationContext ac = new AnnotationConfigApplicationContext(bean.xml); IProcess process = (IProcess) ac.getBean("process");//process是代理类(AOP代理对象),虽然是以target放进去的,但运行时完成织入被修改成代理对象了 Process.doActions(process);
为什么上面其它的切面注释掉只保留了环绕通知?:
ProceedingJoinPoint,要用它是必须要用抛出异常的。有了它,就可以手动设置可以出现哪些同通知,比如try中可以安排后置,catch中可以安排异常通知等等,这样可以替代单独设置后置异常通知等。实际用哪种好自己按需看情况详见logger类中public Object processAdvice方法
方式二:annotation
annotation :1.xml中process与logger由于都是正常bean所以只需各自添加@component()
2@Aspect:用于注释给切面类,本例子中是logger(因为他提供通知方法,process是被切的)
3.在xml文件中: <aop:pointcut id="action2" expression="execution(void cn.liwang.Process.action2())"/>
而用annotation: @Pointcut("execution(void cn.liwang.Process.action2())") private void action2(){}
没错上面两行代码就是为了用annotation加到Logger类中的 @Pointcut配置要劫持的类中的方法,下面跟方法名
这就是使用注解时的不同之处,切poingcut与advice都在同一个类中,所以此类会被@Aspect注解。
通知方法类
// @Before("action2()") public void printBefore() { System.out.println("Logger say Before here."); } // @AfterReturning("action2()") public void printAfterReturning() { System.out.println("Logger say after returning here."); } // @AfterThrowing("action2()") public void printAfterThrowing() { System.out.println("Logger say after throwing here."); } // @After("action2()") public void printAfter() { System.out.println("Logger say after here."); } }
注意
可以将上面几个annotation都注释掉,在方法public Object processAdvice上面添加注释@Around("action2()"):
详见logger类
@Around("action2()") public Object processAdvice(ProceedingJoinPoint pjp)
使用仍然用xml+注解:
ApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml"); Process process = (IProcess) ac.getBean("process"); Process.doActions(process);
下面是不用xml,用配置类
配置类
package cn.liwang; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @ComponentScan(basePackages = "cn.liwang")//自动扫描 @EnableAspectJAutoProxy//自动打开 //三件套相当于代替了xml public class SpringConfiguration { }
使用:
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class); IProcess process = (IProcess) ac.getBean("process"); Process.doActions(process);
JDBCtemplate
JDBC ,Spring的核心类之一
该类在内部已经处理数据库资源的建立和释放,并可以避免一些常见的错误,如关闭连接及抛出异常等。简化了编程JDBC时所需要的基础代码。该类还可以直接通过数据源的引用实例化,然后在服务中使用,也可以通过依赖注入的方式在容器中。
// // 配置 // DriverManagerDataSource ds = new DriverManagerDataSource(); // ds.setDriverClassName("com.mysql.cj.jdbc.Driver"); // ds.setUrl("jdbc:mysql://localhost:3306/db2?serverTimezone=UTC&allowMultiQueries=true"); // ds.setUsername("root"); // ds.setPassword("root"); // // // JDBCTemplate // JdbcTemplate jt = new JdbcTemplate(); // jt.setDataSource(ds);
Spring IoC控制的JDBCTemplate
// ApplicationContext ac = new ClassPathXmlApplicationContext("application1.xml"); // ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class); // JdbcTemplate jt = (JdbcTemplate) ac.getBean("jt"); // jt.execute("INSERT INTO USER (USERNAME, BIRTHDAY, GENDER, ADDRESS)" + // "VALUES" + // "('aaaa', '1981-01-17', '男', 'xxxxxx')"); // 3. JDBCTemplate 增删改查 // 3.1 增删改 // jt.update("INSERT INTO USER (USERNAME, BIRTHDAY, GENDER, ADDRESS)" + // "VALUES" + // "(?, ?, ?, ?)", "aaaa", "1981-01-17", "男", "xxxxxx"); // jt.update("DELETE FROM USER WHERE ID = ?", 24); // jt.update("UPDATE USER SET USERNAME = ? WHERE ID = ?", "bbbb", 23); // 3.2 查询 // class UserRowMapper implements RowMapper<User> {//RowMapper<T>接口提供方法将表中一条记录,装成类,并返回。 // // @Override // public User mapRow(ResultSet resultSet, int i) throws SQLException {//如果查询的记录能对的上,一个User.但是可以不用缺省提供有 // User user = new User(); // user.setId(resultSet.getInt("ID")); // user.setUsername(resultSet.getString("USERNAME")); // user.setBirthday(resultSet.getString("BIRTHDAY")); // user.setGender(resultSet.getString("GENDER")); // user.setAddress(resultSet.getString("ADDRESS")); // return user; // } // } // List<User> users = jt.query("SELECT * FROM USER WHERE USERNAME = ?", new UserRowMapper(),"aaaa");//自己写的 // List<User> users = jt.query("SELECT * FROM USER WHERE USERNAME = ?", new BeanPropertyRowMapper<User>(User.class),"aaaa");//用Spring缺省提供的,上面类就可注释掉(前提数据对的上,别乱查) // // // for(User user: users){ // System.out.println(user); // } // 3.3 查询返回整数 // Long count = jt.queryForObject("SELECT COUNT(*) FROM USER", Long.class); // System.out.println(count);
使用jdbctemplate的事务
创建:本类就是提供切入点的类,先学习1xml文件,使用标签。
package cn.liwang; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Repository @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) public class AccountService { @Autowired private JdbcTemplate jt; public JdbcTemplate getJt() { return jt; } public void setJt(JdbcTemplate jt) { this.jt = jt; } @Transactional(propagation = Propagation.REQUIRED, readOnly = false) public void updateTransfer(Integer id1, Integer id2, Integer balance) {//转钱 jt.update("UPDATE ACCOUNT SET BALANCE = BALANCE - ? WHERE ID = ?", balance, id1);//问号代表要填的参数 // int i = 5 / 0; jt.update("UPDATE ACCOUNT SET BALANCE = BALANCE + ? WHERE ID = ?", balance, id2); } } // 3.4 事务 // AccountService accountService = ac.getBean("accountService", AccountService.class); // // accountService.updateTransfer(1, 3, 300);
tx:advice 标签可以了理解为配置事务属性。 <!-- 可以配置的属性:isolation:事务的隔离界别,DEFAULT,使用数据库的隔离级别 propagation:事务的传递行为:REQUIRED(缺省),如果没有事务,新建一个,如果有,使用现有的。 SUPPORTS,如果没有事务,就按照非事务处理,如果有,使用现有的。 read-only:事务是否只读:增删改 true,查询 false timeout:以秒为单位,默认是-1,不超时 rollback-for:指定回滚的异常的类型,比如X,X发生时回滚,不是X时不会滚。缺省:没有,此时任何异常都需要回滚。 no-rollback-for:指定不回滚的异常的类型,比如Y,Y发生时不会滚,不是Y时回滚。缺省:没有,此时任何异常都需要回滚。 -->
然后学习注解:注解加1.xml文件 告知扫描
然后使用注解加这个配置类:代替1.xml
package cn.liwang; //替换application1.xml import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; @Configuration @ComponentScan(basePackages = "cn.liwang")//扫描包 @EnableTransactionManagement//代替文件中transcationManager public class SpringConfiguration { @Bean(name = "ds") public DataSource createDataSource() { DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName("com.mysql.cj.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/db2?serverTimezone=UTC&allowMultiQueries=true"); ds.setUsername("root"); ds.setPassword("root"); return ds; } @Bean(name = "jt") public JdbcTemplate createJdbcTemplate(DataSource ds){ JdbcTemplate jt = new JdbcTemplate(); jt.setDataSource(ds); return jt; } @Bean(name = "transactionManager") public PlatformTransactionManager createTransactionManager(DataSource ds) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(ds); return transactionManager; }
0303后十分钟 // 3.5 好事的王校长想知道TransactionManager具体是怎么工作的?指的是不去配置,直接Java代码中用,怎么用?(无SpringIoc) // 配置 DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName("com.mysql.cj.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/db2?serverTimezone=UTC&allowMultiQueries=true"); ds.setUsername("root"); ds.setPassword("root"); // JDBCTemplate final JdbcTemplate jt = new JdbcTemplate(); jt.setDataSource(ds); // TransactionManager DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(ds); // TransactionTemplate TransactionTemplate tt = new TransactionTemplate(); tt.setTransactionManager(transactionManager); tt.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus transactionStatus) { // 干活 AccountService accountService = new AccountService(); accountService.setJt(jt); accountService.updateTransfer(1, 3, 300); return null; } });
使用注解Transactional
<groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.13.RELEASE</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.13.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.13.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency>
Spring旅游网站
学到了 mapper实现类可直接作为service类的实例字段(装配即可。不需要使用new)