Spring

作者: 不会写诗的苏轼 | 来源:发表于2023-02-15 11:44 被阅读0次

    Spring程序开发步骤:

    1. 导入Spring开发的基本包坐标编写
      pom文件:
           <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.2.5.RELEASE</version>
            </dependency>
    
    1. Dao接口和实现类
    2. 创建Spring核心配置文件
    3. 在Spring配置文件【applicationContext.xml】中配置UserDaolmpl
    <?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:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
           
         <bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl"></bean>
    
    </beans>
    
    1. 使用Spring的API获得 Bean 实例【调用的是Bean类的无参构造方法创建对象】
     ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");  加载xml配置文件,创建Spring容器和bean标签中的对象
            Userdao userDao = (Userdao) app.getBean("userDao"); 获取容器中的对象
            userDao.add();
    

    配置文件【applicationContext.xml】bean标签的属性

    1. scope:
    取值范围 说明
    singleton 默认值,单例的 app.getBean()获取几次都获取的是同一个对象 在创建Spring容器时对象也被创建了,并放入Spring容器中
    prototype 多例的 ,app.getBean()获取几次获取几个不同的对象 每次getBean()时创建对象
    request WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中
    session WEB 项目中,Spring创建一个Bean的对象,将对象存入到session域中
    global session WEB项目中,应用在Portlet 环境,如果没有Portlet环境那么globalSession相当global session
    1. Bean生命周期配置
      init-method:指定类中的初始化方法名称
      destroy-method:指定类中销毁方法名称
      配置:
      告诉spring初始化时执行init方法,销毁时执行destroy方法
      <bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
    

    UserDaoImpl类:

    package com.xjbt.spring.dao.impl;
    import com.xjbt.spring.dao.Userdao;
    public class UserDaoImpl implements Userdao {
        @Override
        public void add() {
        }
        public UserDaoImpl() {
            System.out.println("无参构造方法");
        }
         配置文件中已经配置了该方法init-method="init"
        public void init(){
            System.out.println("初始化时执行");
        }
    配置文件中已经配置了该方法 destroy-method="destroy"
        public void destroy(){
            System.out.println("销毁时执行");
        }
    }
    
    public class demo1 {
        public static void main(String[] args) {
            ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");   
            获取xml配置文件,然后会执行这些方法,执行顺序:1执行构造方法,创建对象,2执行初始化时方法init,3,执行销毁方法destroy
        }
    

    实例化对象的三种种配置方法:
    1构造方法实例化
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImp1""></bean>
    2,工厂的静态方法实例化
    <bean id="userDao" class="com itheima.factory.StaticFactory" factory-method="getUserDao"></bean> 工厂类中的getUserDao方法时静态方法
    3,工厂实例方法实例化

    <bean id="factory" class="com.itheima.factory.DynamicFactory"></bean>  找到工厂类,设置工厂类的id
    通过  factory-bean="factory"找到工厂类,factory-method="getUserDao"找到工厂类的方法创建userDao对象   getConnection时使用工厂方式创建Connection对象
    <bean id="userDao" factory-bean="factory" factory-method="getUserDao">  
    

    Bean的依赖注入概念

    等框架把持久层对象【dao】传入业务层【service】,而不用我们自己去获取。
    依赖注入(Dependency Injection):它是Spring框架核心IOC的具体实现。

    在编写程序时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。IOC解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。
    那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。

    注入

    主程序:调用service的save()方法
    public class UserController {
        public static void main(String[] args) {
            ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = (UserService) app.getBean("userService");
            userService.save();
        }
    }
    
    1. set注入
             set注入xml
            <bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl"></bean>
            <bean id="userService" class="com.xjbt.spring.service.impl.UserServiceImpl">
                    spring的set注入
                   <property name="userdao" ref="userDao"></property>这里的name是UserService类的setUserDao方法 ref是注入spring的id;
            </bean>
    
       使用set()方法注入
     private Userdao userdao;
        public void setUserdao(Userdao userdao) {
            this.userdao = userdao;
        }
      
        @Override
        public void save() {
            没有使用注入时:获取dao层
           // ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
           // Userdao userDao = (Userdao) app.getBean("userDao");
           // userDao.save();*/
            调用dao层的save()方法
            userdao.save();
    
        }
    
    1. 有参构造方法注入
    <!--构造函数注入-->
            <bean id="userService" class="com.xjbt.spring.service.impl.UserServiceImpl">
                    <constructor-arg name="userdao" ref="userDao"></constructor-arg>
            </bean>
    
      有参构造注入
    private Userdao userdao;
        public UserServiceImpl(Userdao userdao){
            this.userdao=userdao;
        }
    
      @Override
        public void save() {
         userdao.save();
        }
    

    Bean的依赖注入的数据类型

    上面的操作,都是注入的引用Bean,除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。

    1. 普通数据类型
      public class UserDaoImpl implements Userdao {
        private String name;
        private int age;
        public void setName(String name) {
            this.name = name;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Override
        public void save() {
            System.out.println(name+"----"+age);
            System.out.println("dao层方法");
        }
    }
    
    <bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl">
                    <property name="name" value="zhangsan"></property>
                    <property name="age" value="18"></property>
             </bean>
    
    1. 引用数据类型
      public class UserDaoImpl implements Userdao {
        private Map<String,User> mapUser;    =>引用User
        public void setMapUser(Map<String, User> mapUser) {
            this.mapUser = mapUser;
        }
      }
    
    <bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl">
                  <property name="mapUser">
                            <map>
                                    <entry key="u1" value-ref="user1"></entry>
                                    <entry key="u2" value-ref="user2"></entry>
                            </map>
                    </property>
            </bean>
    <bean id="user1" class="com.xjbt.spring.domain.User">
                    <property name="name" value="zhangsan"></property>
                    <property name="age" value="18"></property>
                    <property name="addr" value="xj"></property>
            </bean>
            <bean id="user2" class="com.xjbt.spring.domain.User">
                    <property name="name" value="zhangsan"></property>
                    <property name="age" value="18"></property>
                    <property name="addr" value="xj"></property>
            </bean>
    
    1. 集合数据类型
    public class UserDaoImpl implements Userdao {
        private List listString;
        private Properties propsUser;
    
        public void setListString(List listString) {
            this.listString = listString;
        }
        public void setPropsUser(Properties propsUser) {
            this.propsUser = propsUser;
        }
    
            <bean id="userDao" class="com.xjbt.spring.dao.impl.UserDaoImpl">
                    <property name="listString">
                            <list>
                                    <value>zhangsan</value>
                                    <value>lisi</value>
                                    <value>wangwu</value>
                            </list>
                    </property>
                  
                    <property name="propsUser">
                            <props>
                                    <prop key="p1">pppp1</prop>
                                    <prop key="p2">pppp2</prop>
                                    <prop key="p3">pppp3</prop>
                            </props>
                    </property>
            </bean>
    

    引入其他配置文件(分模块开发)

    实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载

            <import resource="applicationContext-product.xml"></import>
            <import resource="applicationContext-user.xml"></import>
    

    Spring配置数据源

    1.1数据源(连接池)的作用【数据源(连接池)是提高程序性能如出现的。】
    常见的数据源(连接池):DBCP、C3P0、BoneCP Druid等

    • 事先实例化数据源
    • 初始化部分连接资源
    • 使用连接资源时从数据源中获取
    • 使用完毕后将连接资源归还给数据源
         <?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"  =>加context标签
           xmlns:p="http://www.springframework.org/schema/p"
           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"   =>加context标签
          >
          <!--获取jdbc配置文件-->
          <context:property-placeholder location="classpath:jdbc.properties" />  =>获取jdbc配置文件
    
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="${jdbc.driver}"></property>  =>通过spring标签获取jdbc.properties文件中的值
                <property name="url" value="${jdbc_url}"></property>
                <property name="username" value="${jdbc_username}"></property>
                <property name="password" value="${jdbc_password}"></property>
          </bean>
    </beans>
    
     /**
         * Spring获取dataSource数据源
         * @throws Exception
         */
        @Test
        public void springDruidTest() throws Exception {
            ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
            DataSource dataSource = app.getBean(DataSource.class);
            Connection connection = dataSource.getConnection();
            System.out.println(connection);
            connection.close();
        }
    

    Spring注解配置

    Spring原始注解主要是替代<Bean>的配置

    注解 说明
    @Component 使用在类上用于实例化bean
    @Controller 使用在web层类上用于实例化bean
    @Service 使用在service层类上用于实例化bean
    @Repository 使用在dao层上用于实例化bean
    @Autowired 使用在字段上用于根据类型依赖注入 【按照数据类型匹配对象 UserDao有多个实现类,需要加上下边注解】
    @Qualifier 结合@Autowired一起使用用于根据名称依赖注入 按照id进行匹配
    @Resource 相当于@Autowired+@Qualifier,按照名称进行注入
    @Value 注入普通属性 【 可以使用这个属性使用spring表达式获取xml文件中引入的jdbc.properties的内容 @Value("${jdbc.driver}")赋值给标注的属性】
    @Scope 标注bean的作用范围【单例/多列】
    @PostConstruct 使用在方法上标注该方法是bean的初始化方法
    @preDestory 标注该方法是bean的销毁方法

    在使用注解之前需要在xml配置文件中配置需要使用注解的扫描包<context:component-scan base-package="com.xjbt.Spring"/>

    //<bean id="userService" class="com.xjbt.service.impl.UserServiceImpl">
    //@Component("userService")
    @Service("userService") =>实例化serviceBean
    @Scope(singleton【获取之前创建一次对象】|prototype【获取一次创建一次对象】)
    public class UserServiceImpl implements UserService {
        // <property name="userDao" ref="userDao"></property>
        @Autowired
        @Qualifier("userDao")
        @Resource(name="userDao")
        private UserDao userDao;
        使用注解可省略set方法
        public void setUserDao(UserDao userdao) {
            this.userDao = userdao;
        }
     
      @PostConstruct      =>初始化时自动执行该方法
    public  public void init(){
    }
    @preDestory             =>容器销毁时自动执行该方法
    public  public void init(){
    }
        public void save() {
            ApplicationContext app= new ClassPathXmlApplicationContext("applicationContext.xml");
            UserDao userdao = app.getBean(UserDao.class);
            userdao.save();
        }
    }
    

    Spring新注解

    注解 说明
    @Configuration 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
    @ComponentScan 用于指定Spring在初始化容器时要扫描的包。作用和在Spring 的xml配置文件中的<context:component-scan base-package="com.itheima"/>一样
    @Bean 使用在方法上,标注将该方法的返回值存储到Spring容器中
    @PropertySource 用于加载.properties文件中的配置
    @Import 用于导入其他配置类
    @Configuration                                   =>标志该类是Spring的核心配置类
    //<context:component-scan base-package="com.xjbt.Spring"/>
    @ComponentScan("com.xjbt.Spring")                =>扫描注解
    //<context:property-placeholder location="classpath:jdbc.properties" />
    @PropertySource("jdbc.properties")               =>引入外部文件
    @Import({DataSourceConfig.class})                =>导入其他配置
    public class SpringConfig {
       @Value("${jdbc.driver}")                      =>普通属性赋值
        private String driver;
        @Value("${jdbc_url}")
        private String url;
        @Value("${jdbc_username}")
        private String username;
        @Value("${jdbc_password}")
        private String password;
    
    
        @Bean                                            =>将方法的返回值放入spring容器中
        public DruidDataSource getConnection() throws Exception {
            DruidDataSource dataSource=new DruidDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    }
    
            ApplicationContext app =new ClassPathXmlApplicationContext("applicationContext.xml");=>根据配置文件中获取对象
            ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfig.class);   =》根据配置类获取对象
    

    Spring集成junnit

    @RunWith(SpringJUnit4ClassRurner.class)
    //@ContextConfiguration(classes = {value="applicationContext.xml")
    @ContextConfiguration(classes = {SpringCofiguration. class})
    public class SpringJunitTest {
    @Autowired
    private UserService userService;
    @Autowired
    private DataSource dataSource;
    @Test
    public void test1() throws SQLException {
    userService.save() ;
    System out.print1n(dataSource.getConnection() ) ;
    }
    
    

    ApplicationContext应用上下文获取方式【了解】

    如果有多个Servlet获取Spring中创建的对象,就要写多次 ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");来访问配置文件。显然这样是不合理的。

    应用上下文对象是通过方式获取的,但是每次从容器中获得Bean时都要编写new ClasspathXmIApplicationContext(spring配置文件),这样的弊端是配置文件加载多次,应用上下文对象创建多次。

    public class UserServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = app.getBean(UserService.class);
            userService.save();
        }
    }
    

    在在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。
    1.web.xml中配置监听对象。

     <!--配置监听器-->
        <listener>
           <listener-class>com.xjbt.spring_MVC.listener.ContextLoaderListener</listener-class>
       </listener>
    

    2,创建监听对象:

    public class ContextLoaderListener implements ServletContextListener {                       =>实现监听接口
        public void contextInitialized(ServletContextEvent servletContextEvent) {                =>监听服务启动时
            ServletContext servletContext = servletContextEvent.getServletContext();             =>获取servletContext全局对象
            ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml"); =>获取Spring容器对象以及在applicationContext.xml中配置的Bean对象
            servletContext.setAttribute("app",app)                                               =>将Spring容器存入Servlet全局对象 servletContext
        }
        public void contextDestroyed(ServletContextEvent servletContextEvent) {                  =>监听服务结束时
    
        }
    }
    

    3servlet中获取

    public class UserServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext servletContext = req.getServletContext();  =>获取全局对象
            ApplicationContext app=(ApplicationContext)servletContex.getAttribute("app")  =>获取app
            UserService userService = app.getBean(UserService.class);
            userService.save();
        }
    }
    

    优化代码

    1. 监听对象
    public class ContextLoaderListener implements ServletContextListener {                      
        public void contextInitialized(ServletContextEvent servletContextEvent) {                
            ServletContext servletContext = servletContextEvent.getServletContext();      
                  
            ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");   
         ********存在问题:如果修改了applicationContext.xml文件名称,那么必须动监听对象********
            servletContext.setAttribute("app",app)                                               
        }
        public void contextDestroyed(ServletContextEvent servletContextEvent) {               
        }
    }
    

    解决办法:
    将applicationContext.xml配置到web.xml文件中

    设置全局初始化参数
       <context-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>applicationContext.xml</param-value>
       </context-param>
    

    修改监听对象

    public class ContextLoaderListener implements ServletContextListener {                      
        public void contextInitialized(ServletContextEvent servletContextEvent) {                
            ServletContext servletContext = servletContextEvent.getServletContext();  
    
           String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");    通过全局对象获取全局初始化参数      
            
            ApplicationContext app=new ClassPathXmlApplicationContext(contextConfigLocation);   
            servletContext.setAttribute("app",app)                                               
        }
        public void contextDestroyed(ServletContextEvent servletContextEvent) {               
        }
    }
    
    1. servlet
    public class UserServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext servletContext = req.getServletContext();  
            ApplicationContext app=(ApplicationContext)servletContex.getAttribute("app")  
       ********存在问题:属性名称耦合度高********
            UserService userService = app.getBean(UserService.class);
            userService.save();
        }
    }
    

    解决办法:创建工具

    public class WebApplicationUtils {
        public static ApplicationContext getApplication(ServletContext servletContext){
            return (ApplicationContext) servletContext.getAttribute("app");
        }
    }
    

    servlet

    public class UserServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
            ServletContext servletContext = req.getServletContext();
            //ApplicationContext app = (ApplicationContext)servletContext.getAttribute("app");
            ApplicationContext app= WebApplicationUtils.getApplication(servletContext);  
    *************************** getApplication是静态方法,所以不用创建WebApplicationUtils对象直接使用这个方法。*****************
            UserService userService = app.getBean(UserService.class);
            userService.save();
        }
    }
    

    spring使用数据源

    1. 导包
     <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>5.2.5.RELEASE</version>
            </dependency>
    
    1. web.xml配置
      <!--设置全局初始化参数-->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:path:applicationContext.xml</param-value>
        </context-param>
    
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
    1. servlet使用
    public class UserServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext servletContext = req.getServletContext();
            WebApplicationContext app= WebApplicationContextUtils.getWebApplicationContext(servletContext);
            UserService userService = app.getBean(UserService.class);
            userService.save();
        }
    }
    

    AOP

    是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

    AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
    AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    • 作用:在程序运行期间,在不修改源码的情况下对方法(save())进行功能增强(日志控制)
    • 优势:减少重复代码,提高开发效率,并且便于维护

      AOP的底层实现
      SpringMVC运行期间生成代理对象,代理对象的方法先执行增强功能,在调用目标对象的方法,完成对象方法功能增强。

    实际上,AOP的底层是通过Spring提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

    常用的动态代理技术
    JDK代理:基于接口的动态代理技术
    cglib代理:基于父类的动态代理技术
    如果目标对象有接口:jdk通过目标对象的接口动态生成代理对象,
    如果目标对象没有接口:通过cglib给目标对象动态生成一个子对象,这个对象就是代理对象。

    • jdk动态代理:
    public class AdviceTest {
        public static void main(String[] args) {
            //目标对象
            final Target target=new Target();
            //增强对象
            final ProxyAdvice advice=new ProxyAdvice();
    
            //jdk动态代理对象
            TargetInterface proxy=(TargetInterface) Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),//目标对象类加载器
                    target.getClass().getInterfaces(),//目标对象相同的接口字节码数组
                    new InvocationHandler() {
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            advice.before();//增强方法
                            Object invoke=method.invoke(target,args);//执行目标方法   通过这个方法就目标方法已被动态代理
                            advice.after();//增强方法
                            return invoke;
                        }
                    }
            );
    
            proxy.save();
        }
    }
    
    • cglib动态代理
    public class AdviceTest {
        public static void main(String[] args) {
            //目标对象
            final Target target=new Target();
            //增强对象
            final ProxyAdvice advice=new ProxyAdvice();
    
            //cglib动态代理对象
            //1,创建增强器
            Enhancer enhancer=new Enhancer();
            //2,设置父类(目标对象)
            enhancer.setSuperclass(target.getClass());
            //3,设置回调
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    advice.before();
                    Object invoke = method.invoke(target, args);
                    advice.after();
                    return invoke;
                }
            });
            //4,创建代理对象
            Target proxy =(Target) enhancer.create();
            proxy.save();
        }
    

    Spring封装的AOP

    1. AOP相关概念

    Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

    1. 常用的术语如下:
      Target (目标对象):代理的目标对象
      Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
      Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点【目标对象的方法】
      Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义【目标对象要增强的方法】
      Advice(通知/增强)︰所谓通知是指拦截到Joinpoint之后所要做的事情就是【增强方法】
      Aspect(切面):是切入点和通知(引介)的结合【目标方法加入增强方法】
      Weaving (织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
    2. 需要编写的内容
    • 编写核心业务代码(目标类的目标方法)
    • 编写切面类,切面类中有通知(增强功能方法)
    • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
    1. AOP技术实现的内容
      spring框架监测需要增强的方法【切入点】执行时,spring框架创建代理对象,对需要增强的方法进行增强,完成代码运行。

    Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

    1. AOP底层使用哪种代理方式
      在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
    快速入门基于application.xml
    1. 导入AOP相关坐标
     <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
    
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.7</version>
        </dependency>
    
    1. 创建目标接口和目标类(内部有切点)
    2. 创建切面类(内部有增强方法)
    public class MyAspect {
    public void before(){
    System. out.pr int1n("前置增强...... ....");
    }
    
    public void afterReturning() {
    System. out.print1n("后置增强..........");
    )
    / /Proceeding JoinPoint:正在执行的连接点===切点
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
    System. out.print1n("环绕前增强....");
    0bject proceed = pjp.proceed() ; / /切点方法System. out.println("环绕后增强....") ;return proceed;
    
    public void afterThrowing(){
    System. out.println("异常抛出增强......")
    )
    public void after(){
    System. out.println("最终增强.....");
    )
    
    
    
    1. 将目标类和切面类的对象创建权交给spring
    2. 在applicationContext.xml中配置织入关系
     <!--目标对象-->
       <bean id="target" class="com.xjbt.proxy.aop.Target"></bean>
        <!--切面对象-->
        <bean id="myAspect" class="com.xjbt.proxy.aop.MyAspect"></bean>
        <!--织入配置-->
        <aop:config>
            <!--声明切面-->
            <aop:aspect ref="myAspect">
                <!--<aop:before method="before" pointcut="execution(public void com.xjbt.proxy.aop.Target.save())"></aop:before>-->
    <aop:before method="before" pointcut="execution(* com.xjbt.proxy.aop.*.*(..))"></aop:before> 
    pointcut=execution =>修饰符省略 (*)任意返回值.(com.xjbt.proxy.aop)包名.(*)类名.*(..)(任意参数的任意方法名)
    method=> 
            </aop:aspect>
        </aop:config>
    

    5.1抽取表达式

    <aop:config>
            <!--声明切面-->
            <aop:aspect ref="myAspect">
                <!--抽取切点表达式-->
                <aop:pointcut id="myPointcut" expression="execution(* com.xjbt.proxy.aop.*.*(..))"></aop:pointcut><!--切面:切点通知-->
                <aop:around method="after" pointcut-ref="myPointcut"/>
                <aop:after method="after" pointcut-ref="myPointcut" />
            </aop:aspect>
        </aop:config>
    
    1. 测试代码
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class AOPtest {
        @Autowired
        private TargetInterface target;
        @Test
        public void test1(){
            target.save();
        }
    }
    
    快速入门基于注解
    1. 创建目标类(内部有切点)
    @Component("target")
    public class Target{
        public void save() {
            System.out.println("目标方法执行");
        }
    }
    
    1. 创建切面类(内部有增强方法)
    @Component("myAspect")  //注入到spring中
    @Aspect   //表示这是一个前面类
    public class MyAspect {
        @Before("execution(* com.xjbt.proxy.anno.*.*(..))") //配置前置通知
        public void before(){
            System.out.println("动态代理前置对象");
        }
    }
    
    1. 将目标类和切面类的对象创建权交给spring
    2. 在切面类中使用注解配置织入关系
    3. 在配置文件中开启组件扫描和AOP的自动代理
    <!--注解扫描包 识别放入spring容器的类创建对象-->
    <context:component-scan base-package="com.xjbt.proxy.anno"/>
    <!--使用aop注解 -->
    <aop:aspectj-autoproxy />
    
    1. 测试
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext-anno.xml")
    public class AOPtest {
        @Autowired
        private Target target;
        @Test
        public void test1(){
            target.save();
        }
    }
    

    spring事务控制

    1. 编程式事务管理器相关对象
    • PlatformTransactionManager 平台【Platform】事务控制器【TransactionManager】:spring的事务管理器
    PlatformTransactionManager方法 说明
    Transactionstatus getTransaction (TransactionDefination defination) 获取事务的状态信息
    void commit (Transactionstaxus status) 提交事务
    void rollback(Transactionstatus status) 回滚事务

    PlatformTransactionManager是接口类型,不同的 Dao层技术则有不同的实现类
    例如:
    Dao层技术是jdbc或mybatis 时:org.springframework.jdbc.datasource.DatasourceTransactionManager
    Dao层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager

    • TransactionDefinition事务的定义信息对象:
    方法 说明
    int getIsolationLevel() 获得事务的隔离级别
    int getPropogationBehavior() 获得事务的传播行为
    int getTimeout() 获得超时时间
    boolean isReadonly() 是否只读

    1.事务隔离级别
    设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。
    ISOLATION_DEFAULT 【默认】
    ISOLATION_READ_UNCOMMITTED 【读不可提交】
    ISOLATION_READ_COMMITTED 【读并提交】
    ISOLATION_REPEATABLE_READ 【可重复读】
    ISOIATION_SERIALIZABLE 【串行化 全能解决性能底】

    2.事务传播行为【解决调用业务方法时事务统一性的问题】
    a业务方法调用b业务方法,都有事务控制,会出现重复或统一的问题
    REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
    SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
    MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
    REQUERS_NE新建事务,如果当前在事务中,把当前事务挂起。
    NOT SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
    NEVER:以非事务方式运行,如果当前存在事务,抛出异常
    NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作
    超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
    是否只读:建议查询时设置为只读

    • TransactionStatus:事务具体的运行状态
      随着程序的运行实时变化的不需要配置。
    方法 说明
    boolean hassavepoint() 是否存储回滚点
    boolean iscompleted() 事务是否完成
    boolean isNewTransaction() 是否是新事务
    boolean isRollbackonly() 事务是否回滚
    2. 声明式事务控制
    • spring配置文件中配置声明

    事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话也只需要在定义文件中重新配置即可。
    在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便。
    注意: Spring声明式事务控制底层就是AOP。

    快速入门

      <!--配置切点-->
        <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"/>
        </bean>
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        <!--配置事务管理器 增强方法-->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <!--设置事务属性-->
            <tx:attributes>
                <tx:method name="advice" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1" />
                <tx:method name="*"/> <!--任意方法-->
            </tx:attributes>
        </tx:advice>
        <!--AOP织入-->
        <aop:config>
            <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))" />
        </aop:config>
    

    设置事务属性的解释

    name:切点方法名称
    isolation:事务的隔离级别
    propogation:事务的传播行为
    timeout:超时时间
    read-only:是否只读

    • spring注解事务控制
      自定义类可使用注解,非自定义的配置到xml中。
      xml配置文件
      <!--组件扫描-->
    
    <context:component-scan base-package="com.itheima" />
    
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!--使用事务控制注解-->
    <tx:annotation-driven transaction-manager="transactionManager" />
    

    AccountService.java

    @Service("accountService")
    @Transactional(isolation = Isolation.DEFAULT)//所有类下的方法控制事务
    public class AccountServiceImpl implements AccountService {
        @Autowired
        private AccountDao accountDao;
    
        @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)//就近原则
        public void transfer(String outMan, String inMan, double money) {
            accountDao.out(outMan,money);
            accountDao.in(inMan,money);
        }
    }
    

    AccountDao.java

    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        public void out(String outMan, double money) {
            jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
        }
        public void in(String inMan, double money) {
            jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
        }
    }
    

    AccountController.java测试

    public class AccountController {
    
        public static void main(String[] args) {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            AccountService accountService = app.getBean(AccountService.class);
            //int i=1/0;
            accountService.transfer("tom","lucy",500);
        }
    }
    

    ①使用@Transactional在需要进行事务控制的类或是方法上修饰,注解可用的属性同xml配置方式,例如:隔离级别、传播行为等。
    ②注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置。使用在方法上,不同的方法可以采用不同的事务参数配置。
    ③Xml配置文件中要开启事务的注解驱动<tx : annotation-driven />

    相关文章

      网友评论

          本文标题:Spring

          本文链接:https://www.haomeiwen.com/subject/idwihdtx.html