Spring框架的秘密(二)

简介: spring(春天),生于在2002年,由Rod Johnson创作。Spring框架是一个集众多设计模式于一身的开源的、轻量级的项目管理框架。致力于JAVAEE轻量级解决方案。相对于原来学过的框架而言,spring框架和之前学习的struts2 、 mybatis 框架有了本质的区别,不是替换原来的某个框架,而是对其进行整合管理。

StudentServiceImpl.java


package constructor;
public class StudentServiceImpl implements StudentService {
    //依赖注入声明成员变量
    private StudentDAO studentDAO;
    //提供构造方法
    public StudentServiceImpl(StudentDAO studentDAO) {
        this.studentDAO = studentDAO;
    }
    @Override
    public void querys(String name) {
        System.out.println("===querys=service==");
        studentDAO.querys(name);
    }
}


spring.xml


<bean class="autodi.StudentDAOImpl" id="studentDAO"></bean>
<bean class="autodi.StudentServiceImpl" id="studentService">
    <constructor-arg index="0" name="studentDAO" ref="studentDAO"></constructor-arg>
</bean>


测试


package constructor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("constructor/spring.xml");
        StudentService studentService = (StudentService) context.getBean("studentService");
        studentService.querys("晓晓");
    }
}


注入其他属性


<!--使用构造方法注入对象-->
<constructor-arg index="0" name="id" ref="user"/>
<!--使用基本数据类型注入值-->
<constructor-arg index="0" name="id" value="1"/>
<constructor-arg index="1" name="name" value="xiaohei"/>
<constructor-arg index="2" name="age" value="12"/>
<constructor-arg index="3" name="qqs">
    <array>
        <value>xxx</value>
        <value>222</value>
        <value>333</value>
    </array>
</constructor-arg>

注意:构造注入并不常用,不过在一些框架类中必须使用构造注入,这里先了解其注入语法即可。



5.3.自动注入


自动注入对象


语法:


1.依赖:需要谁将谁声明为成员变量并提供公开的Set方法


2.注入:在配置文件中的bean标签,使用autowired属性


原理:底层使用SET方式注入


注意:只能自动完成组件对象之间的注入 不能注入八种基本类型 + String + Date + List + 数组 + Map


autowire : 为类中成员变量自动注入值


autowire=”byName”

根据注入的属性名与配置文件中bean的id匹配,一致则注入,不一致报错


autowire=”byType”

根据注入的属性类型,与配置文件中的类型匹配,类型一致注入(在多个实现类时,会产生歧义)


注意: 无论使用以上那种方式注入都需要为属性提供set方法


StudentDAOmpl.java


package autodi;
public class StudentDAOImpl implements StudentDAO {
    @Override
    public void querys(String name) {
        System.out.println("===querys==dao="+name);
    }
}


StudentServiceImpl.java


package autodi;
public class StudentServiceImpl implements StudentService {
    //依赖注入声明成员变量
    private StudentDAO studentDAO;
    //提供公开的set方法
    public void setStudentDAO(StudentDAO studentDAO) {
        this.studentDAO = studentDAO;
    }
    @Override
    public void querys(String name) {
        System.out.println("===querys=service==");
        studentDAO.querys(name);
    }
}


spring.xml


<bean class="autodi.StudentDAOImpl" id="studentDAO"></bean>
<bean class="autodi.StudentServiceImpl" id="studentService" autowire="byType"></bean>


测试


package constructor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("autodi/spring.xml");
        StudentService studentService = (StudentService) context.getBean("studentService");
        studentService.querys("晓晓");
    }
}


6.Spring 的Bean工厂


6.1.bean的创建模式


singleton:单例 默认

在工厂中全局唯一,只创建一次

prototype: 多例

全局不唯一,每次使用都会创建一个新的对象


<bean id="" class="xxxx.userAction" scope="prototype|singleton">
    service,dao    ----->  singleton
    struts2 action -----> prototype


注意:在项目开发中service,dao组件单例,struts2的Action必须为:多例


6.2.bean的生产原理


原理: 反射+构造方法


 UserDAOImpl userDAO = (UserDAOImpl) Class.forName("com.libin.dao.UserDAOImpl").newInstance();
 System.out.println(userDAO);

6.3.bean的生命周期


  • 何时创建
    随着工厂启动, 所有单例bean随之创建 非单例的bean,每次使用时创建
  • 何时销毁
    工厂**关闭,所有bean随之销毁** ( 注意: spring对多例bean管理松散,不会负责多例bean的销毁)


6.4.bean工厂创建对象的好处


  1. 使用配置文件管理java类,再生产环境中更换类的实现时不需要重新部署,修改文件即可
  2. spring默认使用单例的模式创建bean,减少内存的占用
  3. 通过依赖注入建立了类与类之间的关系(使java之间关系更为清晰,方便了维护与管理)


Day2


1.现有业务层开发存在问题

a.定义业务接口


public interface UserService {
    void save(String name);
    void delete(String id);
    void update();
    String findAll(String name);
    String findOne(String id);
}



b.实现业务接口


public class UserServiceImpl implements UserService {
    @Override
    public void save(String name) {
        try {
            System.out.println("开启事务");
            System.out.println("处理业务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }
    @Override
    public void delete(String id) {
        try {
            System.out.println("开启事务");
            System.out.println("处理业务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }
    @Override
    public void update() {
        try {
            System.out.println("开启事务");
            System.out.println("处理业务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }
    @Override
    public String findAll(String name) {
        try {
            System.out.println("开启事务");
            System.out.println("处理业务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return name;
    }
    @Override
    public String findOne(String id) {
        try {
            System.out.println("开启事务");
            System.out.println("处理业务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return id;
    }
}


问题:从上图中可以看出,现有业务层中控制事务代码出现了大量的冗余,如何解决现有业务层出现的冗余问题?


2.代理引言


a.什么是代理

代理: 指的是java中的一种设计模式

b.为什么需要代理


很多时候除了当前类能够提供的功能外,我们还需要补充一些额外功能。

c.代理的作用

代理对象可以在客户和目标对象之间起到中介作用,从而为目标对象增添额外的功能

d.代理图例


3.静态代理的开发

目标类|对象(target):被代理类称之为目标类|或者被代理的对象的称之为目标对象

开发代理的原则: 代理类和目标类功能一致且实现相同的接口,同时代理类中依赖于目标类对象


a.开发静态代理类


//静态代理类
//开发原则:代理类和目标类实现相同接口,依赖于真正的目标类
public class UserServiceStaticProxy implements UserService {
    //真正的目标类 //target 原始业务逻辑对象
    private UserService userService;
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    @Override
    public void save(String name) {
        try {
            System.out.println("开启事务");
            userService.save(name);//调用真正业务逻辑方法
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }
    @Override
    public void delete(String id) {
        try {
            System.out.println("开启事务");
            userService.delete(id);//调用真正业务逻辑方法
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }
    @Override
    public void update() {
        try {
            System.out.println("开启事务");
            userService.update();//调用真正业务逻辑方法
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }
    @Override
    public String findAll(String name) {
        try {
            System.out.println("开启事务");
            String result = userService.findAll(name);//调用真正业务逻辑方法
            System.out.println("提交事务");
            return result;
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public String findOne(String id) {
        try {
            System.out.println("开启事务");
            //调用目标类方法
            String one = userService.findOne(id);//调用真正业务逻辑方法
            System.out.println("提交事务");
            return one;
        }catch (Exception e){
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return null;
    }
}


b.更改目标实现类


public class UserServiceImpl implements UserService {
    @Override
    public void save(String name) {
        System.out.println("处理业务逻辑,调用DAO~~~");
    }
    @Override
    public void delete(String id) {
        System.out.println("处理业务逻辑,调用DAO~~~");
    }
    @Override
    public void update() {
        System.out.println("处理业务逻辑,调用DAO~~~");
    }
    @Override
    public String findAll(String name) {
        System.out.println("处理业务逻辑,调用DAO~~~");
        return name;
    }
    @Override
    public String findOne(String id) {
        System.out.println("处理业务逻辑,调用DAO~~~");
        return id;
    }
}


c.配置静态代理类


    <!--配置目标类-->
    <bean id="userService" class="staticproxy.UserServiceImpl"></bean>
    <!--配置代理类-->
    <bean id="userServiceStaticProxy" class="staticproxy.UserServiceStaticProxy">
        <!--注入目标对象-->
        <property name="userService" ref="userService"/>
    </bean>


d.调用代理方法

ApplicationContext c

ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userServiceStaticProxy = (UserService) context.getBean("userServiceStaticProxy");
userServiceStaticProxy.save("晓晓");


新的问题:往往在开发我们书写的不仅仅是一个业务层,两个业务层,而我们的业务层会有很多,如果为每一个业务层开发一个静态代理类,不仅没有减轻工作量,甚至让我们的工作量多了一倍不止怎么解决以上这个问题呢?


解决方案: 为业务层在运行过程中动态创建代理类,通过动态代理类去解决我们现有业务层中业务代码冗余的问题 .


4.动态代理的原理


通过jdk提供的Proxy这个类,动态为现有的业务生成代理类

参数一:当前线程类加载器

参数二:生成代理类的接口类型

参数三:通过代理类对象调用方法时会优先进入参数三中的invoke方Proxy.newProxyInstance(loader, interfaces, h);//返回值就是动态代理对象


public class TestDynamicProxy {
    public static void main(String[] args) {
        final UserService userService =  new UserServiceImpl();
        //参数1:当前线程类加载器
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        //参数2:
        Class[] classes =  new Class[]{UserService.class};
        //参数3:
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(contextClassLoader, classes, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                try{
                    System.out.println("开启事务");//附加操作
                    Object invoke = method.invoke(userService, args);
                    System.out.println("提交事务");//附加操作
                    return invoke;
                }catch (Exception e){
                    System.out.println("回滚事务");//附加操作
                }
                return null;
            }
        });
        userServiceProxy.save("小晓");
    }
}


5.AOP (Aspect Oriented Programming)编程


通知(Advice): 除了目标方法以外的操作都称之为通知

切入点(PointCut): 要为哪些类中的哪些方法加入通知

切面(Aspect): 通知 + 切入点


1.通知分类

2.编程步骤


# 1.引入依赖
   spring-aop
   spring-expression
   spring-aspects
# 2.开发通知类
    MethodBeforeAdvice      前置通知
    MethodInterceptor       环绕通知
    AfterReturningAdvice    返回后通知
    ThrowsAdvice            异常通知
    MyAdvice implements  通知接口{.....}
    //自定义通知类:用来完成额外功能
    public class MyAdvice  implements MethodBeforeAdvice {
        @Override//参数1:当前调用的方法对象    //参数2:当前调用方法对象的参数  //参数3:目标对象
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println("目标方法名: "+method.getName());
            System.out.println("目标方法的参数: "+objects);
            System.out.println("目标对象: "+o.getClass());
        }
    }
# 3.配置切面
    a.引入aop命名空间
       <?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">
    b.管理通知
       <!--管理通知类-->
       <bean id="myAdvice" class="before.MyAdvice"/>
    c.配置切面
      <aop:config>
        <aop:pointcut id="pc" expression="execution(* before.UserServiceImpl.*(..))"/>
        <aop:advisor advice-ref="myAdvice" pointcut-ref="pc"/>
      </aop:config>
# 4.启动工厂测试
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("before/spring.xml");
        UserService userSerivce = (UserService) context.getBean("userService");
        System.out.println(userSerivce.getClass());
        userSerivce.save("晓晓");


3.前置通知的使用

4.环绕通知的使用

5.返回后通知

6.异常通知


6.切入点表达表

1.execution方法级别的切入点表达式

注意:方法级别的切入点表达式尽可能精准,否则程序运行可能出现异常



2.within类级别的切入点表达式


# 1.语法
  within(包.类)
# 2.示例
  within(com. service.*) 
      包: com. service
      类: 所有类中所有方法不关心返回值和参数
  within(com. service.UserServiceImpl)
      包: com.libin.service
      类: UserServiceImpl类中所有方法不关心返回值和参数



注意:within的效率高于execution表达式,推荐使用within表达式81.jpg



如果大家觉得还不错,点赞,收藏,分享,一键三连支持我一下~

目录
相关文章
|
2月前
|
Java Spring
聊聊你对SpringBoot框架的理解 ?
SpringBoot是Spring家族中流行的子项目,旨在简化Spring框架开发的繁琐配置。它主要提供三大功能:starter起步依赖简化依赖管理,自动配置根据条件创建Bean,以及内嵌Web服务器支持Jar包运行,极大提升了开发效率。
108 0
|
2月前
|
NoSQL Java 数据库连接
SpringBoot框架
Spring Boot 是 Spring 家族中最流行的框架,旨在简化 Spring 应用的初始搭建与开发。它通过自动配置、起步依赖和内嵌服务器三大核心功能,大幅减少配置复杂度,提升开发效率。开发者可快速构建独立运行的 Web 应用,并支持多种数据访问技术和第三方集成。
|
3月前
|
Java API 网络架构
基于 Spring Boot 框架开发 REST API 接口实践指南
本文详解基于Spring Boot 3.x构建REST API的完整开发流程,涵盖环境搭建、领域建模、响应式编程、安全控制、容器化部署及性能优化等关键环节,助力开发者打造高效稳定的后端服务。
393 1
|
2月前
|
缓存 安全 Java
Spring 框架核心原理与实践解析
本文详解 Spring 框架核心知识,包括 IOC(容器管理对象)与 DI(容器注入依赖),以及通过注解(如 @Service、@Autowired)声明 Bean 和注入依赖的方式。阐述了 Bean 的线程安全(默认单例可能有安全问题,需业务避免共享状态或设为 prototype)、作用域(@Scope 注解,常用 singleton、prototype 等)及完整生命周期(实例化、依赖注入、初始化、销毁等步骤)。 解析了循环依赖的解决机制(三级缓存)、AOP 的概念(公共逻辑抽为切面)、底层动态代理(JDK 与 Cglib 的区别)及项目应用(如日志记录)。介绍了事务的实现(基于 AOP
|
2月前
|
存储 缓存 NoSQL
Spring Cache缓存框架
Spring Cache是Spring体系下的标准化缓存框架,支持多种缓存(如Redis、EhCache、Caffeine),可独立或组合使用。其优势包括平滑迁移、注解与编程两种使用方式,以及高度解耦和灵活管理。通过动态代理实现缓存操作,适用于不同业务场景。
234 0
|
5月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
368 70
|
2月前
|
消息中间件 NoSQL Java
SpringBoot框架常见的starter你都用过哪些 ?
本节介绍常见的Spring Boot Starter,分为官方(如Web、AOP、Redis等)与第三方(如MyBatis、MyBatis Plus)两类,用于快速集成Web开发、数据库、消息队列等功能。
192 0
|
2月前
|
缓存 安全 Java
第五章 Spring框架
第五章 Spring框架
|
2月前
|
缓存 Java 数据库
第五章 Spring框架
第五章 Spring框架

热门文章

最新文章