赞
踩
Spring框架是一个容器,它是用来整合其他框架的框架。
它的核心是IOC(控制反转)和AOP(面向切面编程)。
它是一个大的家族,由20多个模块构成,在很多领域都提供了非常优秀的支持。
它的最终目标其实就是解耦合。
它的核心jar包很小,总共3M多,对代码零污染。
Spring底层是面向接口的开发.
面向接口开发的规范:
1.类中的成员变量设计为接口
2.方法的参数设计为接口
3.方法的返回值设计为接口
4.调用时接口指向实现类
总结一句话,上接口就是为了更加灵活。
1.切面就是程序中那些公共的,通用的,重复的功能。例如 :日志,事务,权限验证等。
2.面向切面编程AOP就是将切面单独拿出来开发,在需要的业务功能上自动反织回去。
控制反转IOC(Inversion of Control)它是一种概念,同时也是一种思想,就是让Spring容器干活的思想。
是创建对象和依赖注入的控制权
是由程序员控制权反转给Spring容器控制权
正转:由程序员创建对象和依赖注入
- Student s = new Student(); //程序员在创建对象
- s.setName("吃切糕的斯派克"); // 程序员依赖注入值
- s.setAge(25); // 程序员依赖注入值
反转:Spring容器创建对象和依赖注入
- <bean id="stu" class="com.spike.pojo.Student"> ===>Spring容器在创建对象
- <property name="name" value="吃切糕的斯派克"></property> ===>Spring容器依赖注入值
- <property name="age" value="25"></property> ===>Spring容器依赖注入值
- </bean>
主要有两种方式
先在pom.xml文件中指定 Java 编译版本,可以是jdk8,jdk11或者jdk17,本处采用jdk17。
- <properties>
- <maven.compiler.source>17</maven.compiler.source>
- <maven.compiler.target>17</maven.compiler.target>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- </properties>
注意点:1.一定要有无参的构造方法
2.所有对象的创建和依赖注入是在容器启动时发生
3.使用<bean>标签创建对象
代码示例
- <!--创建Student对象
- 参数:
- id:创建的对象的名称
- class:创建的对象的类型
- -->
- <bean id="stu" class="com.spike.pojo.Student"></bean>
简单类型(8种基本类型(封装类型)+String)使用value注入值。
引用类型使用ref注入值。
注意点:一定要有setXXX()方法
使用<property>标签赋值
代码示例
- <!--创建School对象-->
- <bean id="school" class="com.spike.pojo.School">
- <property name="name" value="天津大学"></property>
- <property name="address" value="天津市南开区"></property>
- </bean>
- <!--创建Student-->
- <bean id="stu" class="com.spike.pojo.Student">
- <property name="name" value="吃切糕的斯派克"></property>
- <property name="age" value="25"></property>
- <property name="school" ref="school"></property> ===>引用类型注入值
- </bean>
使用<constructor-arg>标签赋值。
a.使用构造方法参数名称注入值
- <!--创建School对象,使用构造方法参数名称注入值
- public School(String name1, String address1) {...} //构造方法
- -->
- <bean id="school" class="com.spike.pojo.School">
- <constructor-arg name="name1" value="天津大学"></constructor-arg>
- <constructor-arg name="address1" value="天津市南开区"></constructor-arg>
- </bean>
b.使用构造方法参数下标注入值
- <!--创建Student,使用构造方法参数下标注入值,默认下标从0开始
- public Student(String name, int age, School school){...}
- -->
- <bean id="stuIndex" class="com.spike.pojo.Student">
- <constructor-arg index="1" value="25"></constructor-arg>
- <constructor-arg index="0" value="吃切糕的斯派克"></constructor-arg>
- <constructor-arg index="2" ref="school"></constructor-arg>
- </bean>
c.使用构造方法参数默认顺序注入值
- <!--创建Student,使用构造方法参数默认顺序注入值
- public Student(String name, int age, School school){...}
- -->
- <bean id="stuSequence" class="com.spike.pojo.Student">
- <constructor-arg value="吃切糕的斯派克"></constructor-arg>
- <constructor-arg value="25"></constructor-arg>
- <constructor-arg ref="school"></constructor-arg>
- </bean>
Spring的IOC的功能就是创建对象并依赖注入值,并不在乎是系统类型还是自定义类型。
- <!--例如创建Date类型的对象-->
- <bean id="myDate" class="java.util.Date">
- <property name="time" value="1234567891011"></property>
- </bean>
- <!--注意setTime()方法不一定给time的成员变量赋值,只是调用setTime()的方法-->
Spring创建对象默认的作用域范围是单例的(singleton)。可以设置为非单例(prototype)。
- <bean id="myDate" class="java.util.Date" scope="singleton"></bean>
- <bean id="myDate" class="java.util.Date" ></bean>
- <!--每次取都返回同一个第一次创建好的对象-->
- <bean id="myDate" class="java.util.Date" scope="prototype"></bean>
- <!--每次取都返回一个新创建的对象-->
前提:引用类型才可以实现自动注入。
自动注入有两种方式:按类型自动注入 ,按名称自动注入。
- <bean id="school" class="com.bjpowernode.s02.School">
- <property name="name" value="天津大学"></property>
- <property name="address" value="天津市南开区"></property>
- </bean>
- <!--创建Student-->
-
- <!--按名称自动注入-->
- <bean id="stu" class="com.spike.pojo.Student" autowire="byName">
- <property name="name" value="吃切糕的斯派克"></property>
- <property name="age" value="25"></property>
- </bean>
-
- <!--按类型自动注入-->
- <bean id="stu" class="com.spike.pojo.Student" autowire="byType">
- <property name="name" value="吃切糕的斯派克"></property>
- <property name="age" value="25"></property>
- </bean>
-
依赖注入:DI(Dependency Injection)是IOC的实现技术,所以说DI就是IOC。两种不同的说法而已,就是Spring去创建对象并依赖注入。
@Comconpent:创建任意对象,默认对象的名称是类名的驼峰命名法,也可以自定义对象名称
@Controller:专门用于创建界面层的对象
@Service:专门用来创建业务逻辑层的对象
@Repository:专门用来数据访问层的对象
@Value:简单类型注入
@Autowired:引用类型按类型注入
注意:
@Autowired
@Qualifier("名称")
俩注解一起用是引用类型的按名称注入
代码示例
- @Component
- public class Student {
- @Value("吃切糕的斯派克")
- private String name;
- @Value("25")
- private int age;
- @Autowired //===>单独使用是按类型注入
- @Qualifier("school") //===>俩注解一起使用是按名称注入
- private School school;
- }
切记!!!!一定要在applicationContext.xml配置文件中添加包扫描。
<context:component-scan base-package="com.spike.pojo"></context:component-scan>
1.被注入的对象的类型(Student中的School对象)与注入的对象(Bean工厂中创建好的对象)的类型完全一致。
2.被注入的对象的类型(Student中的School对象--父)与注入的对象(Bean工厂中创建好的对象--子)的类型是父子类类型。
按类型注入时如果有多个可被注入的对象,再次按名称进行筛选注入
按名称注入时如果有多个可被注入的对象,直接按名称指定注入
3.被注入的对象的类型(Student中的School对象--接口)与注入的对象(Bean工厂中创建好的对象--实现类)的类型是接口和实现类。
- <context:component-scan base-package="com.spike.mapper"></context:component-scan>
- <context:component-scan base-package="com.spike.service.impl"></context:component-scan>
- <context:component-scan base-package="com.spike.controller"></context:component-scan>
<context:component-scan base-package="com.spike.mapper;com.spike.service.impl,com.spike.controller">
<context:component-scan base-package="com.spike"></context:component-scan>
因为项目越来越大,多人协作开发,要进行配置文件拆分。
- <!--applicationContext_mapper.xml-->
- <bean id="uMapper" class="com.spike.mapper.UsersMapperImpl"></bean>
- <bean id="bMapper" class="com.spike.mapper.BookMapperImpl"></bean>
- <!--applicationContext_service.xml-->
- <bean id="uService" class="com.spike.service.impl.UsersServiceImpl">
- <property name="usersMapper" ref="uMapper"></property>
- </bean>
- <bean id="bService" class="com.spike.service.impl.BookServiceImpl">
- <property name="bookMapper" ref="bMapper"></property>
- </bean>
- <!--applicationContext_controller.xml-->
- <bean id="usersController" class="com.spike.controller.UsersController">
- <property name="usersService" ref="uService"></property>
- </bean>
- <bean id="bookController" class="com.spike.controller.BookController">
- <property name="bookService" ref="bService"></property>
- </bean>
- <!--applicationContext_users.xml-->
- <bean id="uMapper" class="com.spike.mapper.UsersMapperImpl"></bean>
- <bean id="uService" class="com.spike.service.impl.UsersServiceImpl">
- <property name="usersMapper" ref="uMapper"></property>
- </bean>
- <bean id="usersController" class="com.spike.controller.UsersController">
- <property name="usersService" ref="uService"></property>
- </bean>
- <!--applicationContext_book.xml-->
- <bean id="bMapper" class="com.spike.mapper.BookMapperImpl"></bean>
- <bean id="bService" class="com.spike.service.impl.BookServiceImpl">
- <property name="bookMapper" ref="bMapper"></property>
- </bean>
- <bean id="bookController" class="com.spike.controller.BookController">
- <property name="bookService" ref="bService"></property>
- </bean>
- <import resource="applicationContext_mapper.xml">
- <import resource="applicationContext_service.xml">
- <import resource="applicationContext_controller.xml">
<import resource="applicationContext_*.xml">
注解优点:
方便
直观
高效(代码少,没有配置文件的书写那么复杂)。
注解弊端: 以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。
XML方式优点:
配置和代码是分离的。
在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
XML方式缺点:编写麻烦,效率低,大型项目过于复杂。
AOP(Aspect Orient Programming),面向切面编程。
切面就是程序中那些公共的通用的,重复的代码和功能称为切面。例如:日志,事务 ,权限等。
面向切面编程:将切面单拎出来开发,在需要的方法中自动反织回去.
通过运行期动态代理实现程序功能的统一维护的一种技术。
面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务逻辑中。提高程序的可重用性,同时提高了开发的效率。
目的:完成业务和切面功能的解耦合
业务:图书或商品购买业务功能
切面:事务或日志或权限
代理设计模式是23种设计模式之一。目标对象不可访问,通过代理对象增强功能访问就是代理设计模式。
代理模式分为静态代理和动态代理。动态代理分为JDK动态代理和CGLib动态代理。
静态代理的设计规范:
(1)必须有业务接口
(2)代理对象和目标(业务)对象实现同一个业务接口
(3)静态代理对象在程序运行前就已经存在
(4)静态代理负责业务和切面的整合
JDK动态代理的设计规范:
(1)必须有业务接口
(2)动态代理对象在程序运行时动态的在内存中构建
(3)动态代理通过方法传参进行业务和切面的整合
(4)动态代理对象不能代理业务接口外的方法
JDK动态代理涉及到的类和接口
(1)Proxy类 java.lang.reflect包下的类
用来生成动态代理对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
(2)InvocationHandler接口
代理处理器,用来进行切面和业务整合
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
(3)Method类
用来传递测试类中正在调用的目标方法buy(),one()
method.invoke(); ===>进行手工调用目标方法
代码示例 手写AOP-------->循序渐进
第一个版本:业务和切面耦合在一起
- /**
- * 第一个版本:业务和切面耦合在一起
- */
- public class BookServiceImpl {
- public void buy(){
- try {
- //切面
- System.out.println("事务开启............");
- //业务
- System.out.println("图书购买业务功能实现 ...........");
- //切面
- System.out.println("事务提交............");
- } catch (Exception e) {
- System.out.println("事务回滚...........");
- }
- }
- }
第二个版本:使用静态子类代理拆分业务和切面
- /**
- * 第二个版本:通过子类实现切面功能,父类只做业务实现
- */
- public class BookServiceImpl {
- public void buy(){
- //只完成主业务功能
- System.out.println("图书购买功能实现..............");
- }
- }
- /**
- * 第二个版本:子类完成切面功能(事务)的处理,整合父类的业务功能
- */
- public class SubBookServiceImpl extends BookServiceImpl {
- @Override
- public void buy() {
- //子类中提供事务的切面
- try {
- System.out.println("事务开启...........");
- super.buy();//父类中主业务功能实现
- System.out.println("事务提交...........");
- } catch (Exception e) {
- System.out.println("事务回滚...........");
- }
- }
- }
第三个版本:使用静态代理,完成业务和切面的解耦合
- /**
- * 第三个版本:接口规定业务功能
- */
- public interface Service {
- //购买的业务
- void buy();
- }
- public class BookServiceImpl implements Service {
- @Override
- public void buy() {
- System.out.println("图书购买业务功能实现 ..............");
- }
- }
- public class ProductServiceImpl implements Service {
- @Override
- public void buy() {
- System.out.println("商品业务功能实现.........");
- }
- }
- /**
- * 第三个版本的静态代理类,在本类中完成业务和切面的整合
- * 灵活目标对象的切换
- */
- public class Agent implements Service{
- //类中的成员变量设计为接口
- Service target;
- //通过构造方法传入目标对象,方法的参数设计为接口
- public Agent(Service target){
- this.target = target;
- }
- @Override
- public void buy() {
- try {
- //事务切面
- System.out.println("事务开启.............");
- //调用目标对象的业务功能
- // BookServiceImpl bookService = new BookServiceImpl();
- // bookService.buy();
- // ProductServiceImpl productService = new ProductServiceImpl();
- // productService.buy();
- target.buy();//图书对象来了,就是图书功能实现 ,商品对象来了,就是商品功能实现
- //事务切面
- System.out.println("事务提交............");
- } catch (Exception e) {
- System.out.println("事务回滚............");
- }
- }
- }
- @Test
- public void test02(){
- //调用时接口指向实现类
- Service agent = new Agent(new ProductServiceImpl());
- agent.buy();
-
- }
第四个版本 使用静态代理,以面向接口的方式进行业务和切面的各种拆分整合
- /**
- * 切面接口定义
- */
- public interface AOP {
- //JDK8以后的新特性,默认接口实现default
- default void before(){}
- default void after(){}
- default void exception(){}
- }
- /**
- * 事务的切面实现
- */
- public class TransAop implements AOP {
- @Override
- public void before() {
- System.out.println("事务开启............");
- }
- @Override
- public void after() {
- System.out.println("事务提交............");
- }
- @Override
- public void exception() {
- System.out.println("事务回滚............");
- }
- }
- /**
- * 日志的切面实现
- */
- public class LogAop implements AOP {
- @Override
- public void before() {
- System.out.println("前置日志输出...........");
- }
- }
- /**
- * 静态代理对象,完成业务和切面的整合
- * 通过接口灵活的进行业务功能切换和切面功能切换
- */
- public class Agent implements Service {
- //成员变量设计为接口(面向接口编程)
- Service target; //灵活切换目标对象
- AOP aop; //灵活切换切面对象
-
- //构造方法传入参数(方法的参数设计为接口--->面向接口编程)
- public Agent(Service target,AOP aop){
- this.target = target;
- this.aop = aop;
- }
- @Override
- public void buy() {
- try {
- //切面
- aop.before(); //不同的切面功能
- //业务
- target.buy(); //不同的业务功能
- //切面
- aop.after();
- } catch (Exception e) {
- aop.exception();
- }
- }
- }
- @Test
- public void test02(){
- //通过静态代理对象完成功能调用
- Service agent = new Agent(new ProductServiceImpl(),new LogAop());
- agent.buy();
- }
第五个版本 使用动态代理,解耦合业务和切面
- public class ProxyFactory {
- public static Object getAgent(Service yewu,AOP aop){
- return Proxy.newProxyInstance(
- yewu.getClass().getClassLoader(), // ClassLoader loader,类加载器,将目标对象加载进JVM被解析执行
- yewu.getClass().getInterfaces(), //Class<?>[] interfaces, 目标对象实现的所有业务接口
- // InvocationHandler h 代理处理器,进行业务和切面整合
- new InvocationHandler() {
- @Override
- public Object invoke(
- //生成的动态代理对象(本次没用)
- Object proxy,
- //外部测试类中正在调用的目标方法通过method对象传进来,可以手工在此调用 method.invoke();===>buy()
- Method method,
- //目标方法的参数
- Object[] args) throws Throwable {
- //进行业务和切面整合
- Object obj = null;
- try {
- //切面
- aop.before();
- //业务方法 buy() 通过反射调用目标方法 method.invoke(yewu,args)===> buy();
- obj = method.invoke(yewu,args);
- //切面
- aop.after();
- } catch (Exception e) {
- //切面
- aop.exception();
- }
-
- return obj; //目标方法的返回值
- }
- }
- );
- }
- }
Before通知:在目标方法被调用前调用,涉及接口org.springframework.aop.MethodBeforeAdvice;
After通知:在目标方法被调用后调用,涉及接口org.springframework.aop.AfterReturningAdvice;
Throws通知:目标方法抛出异常时调用,涉及接口org.springframework.aop.ThrowsAdvice;
Around通知:拦截对目标方法调用,涉及接口为org.aopalliance.intercept.MethodInterceptor;
它是专门针对AOP的非常优秀的框架,易学易用。
它是基于java语言开发的,可以无缝扩展功能。
AspetJ 是 Eclipse 的开源项目。
(1)@Before:前置通知
(2)@AfterReturning:后置通知
(3)@Around:环绕通知
(4)@After:最终通知
(5)@Pointcut:给切入点表达式起别名
用来指定切入切面的位置
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
简化版公式:
execution(方法返回值 方法声明(参数))
常见符号解析:
* 代表任意字符(通配符)
.. 出现在方法的参数中,代表任意参数
出现在路径中,代表当前路径及其子路径及其所有的类
代码示例
- execution(public * *(..)) //所有公共访问权限的方法
- execution(* set*(..)) //以set开头的任意方法
- execution(* com.spike.service.impl.*.*(..)) //com.spike.service.impl下的所有类中的所有方法
- execution(* com.spike.service..*.*(..)) //com.spike.service及其子包和所有类
- execution(* *..service.*.*(..)) //以service结尾的包,前面可以有多级包
- execution(* *.service.*.*(..)) //service前面只能有一级包
在目标方法前切入切面功能,不能影响目标方法的返回值。
- @Aspect //交给AspectJ框架去处理切面功能
- @Component
- public class MyAspect {
- /**
- * 实现切面功能要依靠切面方法
- * 前置通知切面方法的规范:
- * 1)访问权限是public
- * 2)切面方法没有返回值void
- * 3)切面方法名称自定义
- * 4)切面可以没有参数,如果有参数只能是JoinPoint类型
- * 5)使用@Before注解声明是前置通知
- * 参数:
- * value:指定切入点表达式
- * 目标方法:
- * public String doSome(String name, int age)
- */
- // @Before(value = "execution(public String com.spike.service.SomeServiceImpl.doSome(String,int))")
- //@Before(value = "execution(public String com.spike.service.SomeServiceImpl.*(String,int))")
- //@Before(value = "execution(public String com.spike.service.SomeServiceImpl.*(..))")
- //@Before(value = "execution(public * com.spike.service.SomeServiceImpl.*(..))")
- //@Before(value = "execution(public * com.spike.service.*.*(..))")
- //@Before(value = "execution(public * com.spike.service..*.*(..))")
- @Before(value = "execution(public * *..service..*.*(..))")
- public void myBefore(){
- System.out.println("前置通知功能实现");
- }
- }
在目标方法执行后切入切面功能,在切面方法中可以得到目标方法的返回值。
目标方法的返回值是通过切面方法的参数传入,所以受传参的值传递和引用传递的影响,值传递 (String+8种基本类型)不能影响目标方法的返回值,但引用传递可以改变目标方法的返回值。
- @Aspect
- @Component
- public class MyAspect {
- /**
- * 后置通知切面方法的规范
- * 1)访问权限是public
- * 2)切面方法没有返回值void
- * 3)切面方法名称自定义
- * 4)切面方法可以没有参数,如果有参数就是目标方法的返回值
- * 5)使用@AfterReturning注解声明是后置通知
- * 参数
- * value:指定切入点表达式
- * returning:指定返回的目标方法的返回值的名称,此名称与切面方法参数名称一致
- */
- @AfterReturning(value = "execution(* com.spike.service.*.*(..))",returning ="obj" )
- public void myAfterReturning(Object obj){
- //先判断是否有返回值
- if(obj != null){
- //再判断是否是String类型
- if(obj instanceof String){
- String s = (String) obj;
- System.out.println("在切面方法中目标方法的返回值是:"+s.toUpperCase());
- }
- //再判断是否是Student类型
- if(obj instanceof Student){
- Student stu = (Student) obj;
- stu.setName("吃切糕的斯派克");
- System.out.println("在切面方法中目标方法的返回值是:"+stu);
- }
- }
- System.out.println("后置通知功能实现");
- }
- }
是通过拦截目标方法,在其前后切入切面功能,它是功能最强大的通知,事务就是使用的环绕通知
在切面方法中可以随意改变目标方法的返回值。
切面方法的参数就是目标方法本身。
切面方法的返回值就是目标方法的返回值。
切面方法中可以控制目标方法的访问。
- //需求:根据目标方法的第一个参数是吃切糕的斯派克,则可访问目标方法,否则拒绝访问目标方法.
- @Aspect
- @Component
- public class MyAspect {
- /**
- * 环绕通知切面方法规范
- * 1)访问权限是public
- * 2)切面方法有返回值,其返回值就是目标方法的返回值
- * 3)切面方法名称自定义
- * 4)切面 法有参数,参数就是目标方法本身
- * 5)要回避异常Throwable
- * 6)使用@Around注解声明是环绕通知
- * 参数:
- * value:指定切入点表达式
- */
- @Around(value = "execution(* com.spike.service.*.*(..))")
- public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
- //取出目标方法的参数
- Object[] params = pjp.getArgs();
- if (params.length > 1) {
- String name = (String) params[0];
- if ("吃切糕的斯派克".equals(name)) {
- //前切功能
- System.out.println("环绕通知中的前置功能");
- //调用目标方法
- Object obj = pjp.proceed();
- //后切功能
- System.out.println("环绕通知中的后置功能");
- return obj.toString().toUpperCase();
- }
- }
- System.out.println("您无权访问目标方法");
- return null;
- }
- }
是在目标方法执行后切入切面,不管目标方法是否正常执行,切面方法中的功能都会执行,它有点类似于finally,是程序的一个出口,用来进行善后处理,关闭流,清空对象等。
- @Aspect
- @Component
- public class MyAspect {
- /**
- * 实现切面功能要依靠切面方法
- * 最终通知切面方法的规范:
- * 1)访问权限是public
- * 2)切面方法没有返回值void
- * 3)切面方法名称自定义
- * 4)切面可以没有参数,如果有参数只能是JoinPoint类型
- * 5)使用@After注解声明是最终通知
- * 参数:
- * value:指定切入点表达式
- *
- */
- @After(value = "execution(* com.spike.service.*.*(..))")
- public void myAfter(){
- System.out.println("最终通知功能实现");
- }
- }
如果多个切面切入到同一个切入点,则可以使用@Pointcut注解起别名,使用一个无参无返空实现的方法名称作为别名。
- @Aspect
- @Component
- public class MyAspect {
- /**
- * 实现切面功能要依靠切面方法
- * 最终通知切面方法的规范:
- * 1)访问权限是public
- * 2)切面方法没有返回值void
- * 3)切面方法名称自定义
- * 4)切面可以没有参数,如果有参数只能是JoinPoint类型
- * 5)使用@After注解声明是最终通知
- * 参数:
- * value:指定切入点表达式
- *
- */
- @After(value = "mycut()")
- public void myAfter(){
- System.out.println("最终通知功能实现");
- }
- @Before(value = "mycut()")
- public void myBefore1(){
- System.out.println("前置通知功能实现1");
- }
- @Before(value = "mycut()")
- public void myBefore2(){
- System.out.println("前置通知功能实现2");
- }
- @AfterReturning(value = "mycut()")
- public void myAfterReturning(){
- System.out.println("后置通知功能实现");
- }
- @Pointcut(value = "execution(* com.spike.service.*.*(..))")
- public void mycut(){}
- }
AspectJ框架默认使用的是JDK动态代理,可以设置为CGLib动态代理。
(1)JDK动态代理必须实现接口
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> ===>JDK动态代理
代理对象必须使用接口来接。
SomeService service = (SomeService) ac.getBean("someServiceImpl");
动态代理对象的类型是:class jdk.proxy2.$Proxy19
如果使用实现类来接动态代理对象则报错:class jdk.proxy2.$Proxy19 cannot be cast to class com.spike.service.SomeServiceImpl
(2)设置为CGLib动态代理,可以不实现接口,它是通过动态生成子类代理重写父类中业务方法来扩展功能。
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> ===>CGLib动态代理
可以使用接口或实现类来接动态代理对象
SomeServiceImpl service = (SomeServiceImpl) ac.getBean("someServiceImpl");
SomeService service = (SomeService) ac.getBean("someServiceImpl");
事务的处理是由Spring来控制,Spring事务控制的方式有两种:编程式事务和声明式事务
1.在配置文件中添加事务管理器
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <!--必须配置数据源-->
- <property name="dataSource" ref="dataSource"></property>
- </bean>
2.在配置文件中添加事务的注解驱动
<tx:annotation-driven></tx:annotation-driven>
3.在类上或方法上添加@Transactional注解声明事务
- @Transactional
- public Integer saveAccounts(Accounts accounts) {
- Integer num = accountsMapper.saveAccounts(accounts);
- System.out.println(accounts.getaName()+"帐户增加成功!num="+num);
- //手工抛出异常
- System.out.println(1/0);
- return num;
- }
4.@Transactional注解参数详解
- @Transactional(
- noRollbackForClassName = "ArithmeticException", //发生指定的异常不回滚事务,使用异常的名称
- noRollbackFor = ArithmeticException.class, //发生指定的异常不回滚事务,使用异常的类型
- rollbackForClassName = "NullPointerException", //发生指定的异常必须回滚事务,使用异常的名称
- rollbackFor = NullPointerException.class, //发生指定的异常必须回滚事务,使用异常的类型
- isolation = Isolation.DEFAULT , //使用数据库默认的事务隔离级别
- timeout = -1, //设置连接超时时间,默认-1,永不超时
- readOnly = false, //查询时必须设置只读属性为true,默认是false
- propagation = Propagation.REQUIRED //事务的传播特性
- )
事务的传播特性可以决定多个事务之间的互斥,归并,加入等。
常用
PROPAGATION_REQUIRED:必被包含事务
PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不 单开事务
PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境
不常用
PROPAGATION_MANDATORY:必须包在事务中,没有事务则抛异常
PROPAGATION_NESTED:嵌套事务
只需要在配置文件中声明,不需要硬编码实现事务管理。
需要方法有命名规范:
增加: insert save add create
更新: update change modify set
删除: delete drop remove clear
查询: select search find query get
代码示例
- <!-- 基于注解的开发添加包扫描-->
- <context:component-scan base-package="com.spike.service.impl"></context:component-scan>
- <!-- 添加事务管理器-->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource"></property>
- </bean>
- <!-- 添加事务的切面-->
- <tx:advice id="myadive" transaction-manager="transactionManager">
- <tx:attributes>
- <tx:method name="*select*" read-only="true"/>
- <tx:method name="*find*" read-only="true"/>
- <tx:method name="*get*" read-only="true"/>
- <tx:method name="*search*" read-only="true"/>
- <tx:method name="*query*" read-only="true"/>
- <tx:method name="*insert*" propagation="REQUIRED" />
- <tx:method name="*add*" propagation="REQUIRED"/>
- <tx:method name="*save*" propagation="REQUIRED" />
- <tx:method name="*create*" propagation="REQUIRED"/>
- <tx:method name="*update*" propagation="REQUIRED"/>
- <tx:method name="*change*" propagation="REQUIRED"/>
- <tx:method name="*modify*" propagation="REQUIRED"/>
- <tx:method name="*set*" propagation="REQUIRED"/>
- <tx:method name="*delete*" propagation="REQUIRED"/>
- <tx:method name="*drop*" propagation="REQUIRED"/>
- <tx:method name="*remove*" propagation="REQUIRED"/>
- <tx:method name="*clear*" propagation="REQUIRED"/>
- <tx:method name="*" propagation="SUPPORTS"/>
- </tx:attributes>
- </tx:advice>
- <!-- 切面和切入点绑定-->
- <aop:config>
- <aop:pointcut id="mycut" expression="execution(* com.spike.service.impl.*.*(..))"/>
- <aop:advisor advice-ref="myadive" pointcut-ref="mycut"></aop:advisor>
- </aop:config>
但凡要进行事务的处理,必须添加事务管理器。
事务管理器是根据不同的技术或框架来生成提交和回滚的对象。
JDBC: Connection con.commit(); con.rollback();
MyBatis: SqlSession sqlSession.commit(); sqlSession.rollback();
Hibernate: Session session.commit(); session.rollback();
MyBatis框架的事务管理器是:DataSourceTransactionManager
(1)工厂模式:Spring通过工厂模式BeanFactory,ApplicationContext创建Bean对象。
(2)代理设计模式:SpringAOP的实现,底层使用了动态代理模式。
(3)单例模式:Spring中的Bean默认都是单例的。
(4)模板方法模式:Spring中jdbcTemplate,hibernateTemplate等以Template结尾的类都用到了模板模式。
(5)装饰模式:我们的项目需要连接多个数据库,而不同的客户在访问时可能会访问不同的数据库,这种模式可以让我们根据用户的需求动态的切换数据库。
(6)观察者模式:Spring的事件驱动是观察者模式的应用。
(7)适配器模式:SpringAOP的增强功能使用到了适配器模式。
笔者的话
写到这里已经2w字了,可能还有些知识点未列出,笔者在后续会进行更新。同时也恭喜你成功的学到了最后
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。