Spring

Spring程序開(kāi)發(fā)步驟:

  1. 導(dǎo)入Spring開(kāi)發(fā)的基本包坐標(biāo)編寫
    pom文件:
       <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
  1. Dao接口和實(shí)現(xiàn)類
  2. 創(chuàng)建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 實(shí)例【調(diào)用的是Bean類的無(wú)參構(gòu)造方法創(chuàng)建對(duì)象】
 ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");  加載xml配置文件,創(chuàng)建Spring容器和bean標(biāo)簽中的對(duì)象
        Userdao userDao = (Userdao) app.getBean("userDao"); 獲取容器中的對(duì)象
        userDao.add();

配置文件【applicationContext.xml】bean標(biāo)簽的屬性

  1. scope:
取值范圍 說(shuō)明
singleton 默認(rèn)值,單例的 app.getBean()獲取幾次都獲取的是同一個(gè)對(duì)象 在創(chuàng)建Spring容器時(shí)對(duì)象也被創(chuàng)建了,并放入Spring容器中
prototype 多例的 ,app.getBean()獲取幾次獲取幾個(gè)不同的對(duì)象 每次getBean()時(shí)創(chuàng)建對(duì)象
request WEB項(xiàng)目中,Spring創(chuàng)建一個(gè)Bean的對(duì)象,將對(duì)象存入到request域中
session WEB 項(xiàng)目中,Spring創(chuàng)建一個(gè)Bean的對(duì)象,將對(duì)象存入到session域中
global session WEB項(xiàng)目中,應(yīng)用在Portlet 環(huán)境,如果沒(méi)有Portlet環(huán)境那么globalSession相當(dāng)global session
  1. Bean生命周期配置
    init-method:指定類中的初始化方法名稱
    destroy-method:指定類中銷毀方法名稱
    配置:
  告訴spring初始化時(shí)執(zhí)行init方法,銷毀時(shí)執(zhí)行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("無(wú)參構(gòu)造方法");
    }
     配置文件中已經(jīng)配置了該方法init-method="init"
    public void init(){
        System.out.println("初始化時(shí)執(zhí)行");
    }
配置文件中已經(jīng)配置了該方法 destroy-method="destroy"
    public void destroy(){
        System.out.println("銷毀時(shí)執(zhí)行");
    }
}
public class demo1 {
    public static void main(String[] args) {
        ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");   
        獲取xml配置文件,然后會(huì)執(zhí)行這些方法,執(zhí)行順序:1執(zhí)行構(gòu)造方法,創(chuàng)建對(duì)象,2執(zhí)行初始化時(shí)方法init,3,執(zhí)行銷毀方法destroy
    }

實(shí)例化對(duì)象的三種種配置方法:
1構(gòu)造方法實(shí)例化
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImp1""></bean>
2,工廠的靜態(tài)方法實(shí)例化
<bean id="userDao" class="com itheima.factory.StaticFactory" factory-method="getUserDao"></bean> 工廠類中的getUserDao方法時(shí)靜態(tài)方法
3,工廠實(shí)例方法實(shí)例化

<bean id="factory" class="com.itheima.factory.DynamicFactory"></bean>  找到工廠類,設(shè)置工廠類的id
通過(guò)  factory-bean="factory"找到工廠類,factory-method="getUserDao"找到工廠類的方法創(chuàng)建userDao對(duì)象   getConnection時(shí)使用工廠方式創(chuàng)建Connection對(duì)象
<bean id="userDao" factory-bean="factory" factory-method="getUserDao">  

Bean的依賴注入概念

等框架把持久層對(duì)象【dao】傳入業(yè)務(wù)層【service】,而不用我們自己去獲取。
依賴注入(Dependency Injection):它是Spring框架核心IOC的具體實(shí)現(xiàn)。

在編寫程序時(shí),通過(guò)控制反轉(zhuǎn),把對(duì)象的創(chuàng)建交給了Spring,但是代碼中不可能出現(xiàn)沒(méi)有依賴的情況。IOC解耦只是降低他們的依賴關(guān)系,但不會(huì)消除。例如:業(yè)務(wù)層仍會(huì)調(diào)用持久層的方法。
那這種業(yè)務(wù)層和持久層的依賴關(guān)系,在使用Spring之后,就讓Spring來(lái)維護(hù)了。

注入

主程序:調(diào)用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() {
        沒(méi)有使用注入時(shí):獲取dao層
       // ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
       // Userdao userDao = (Userdao) app.getBean("userDao");
       // userDao.save();*/
        調(diào)用dao層的save()方法
        userdao.save();

    }
  1. 有參構(gòu)造方法注入
<!--構(gòu)造函數(shù)注入-->
        <bean id="userService" class="com.xjbt.spring.service.impl.UserServiceImpl">
                <constructor-arg name="userdao" ref="userDao"></constructor-arg>
        </bean>
  有參構(gòu)造注入
private Userdao userdao;
    public UserServiceImpl(Userdao userdao){
        this.userdao=userdao;
    }

  @Override
    public void save() {
     userdao.save();
    }

Bean的依賴注入的數(shù)據(jù)類型

上面的操作,都是注入的引用Bean,除了對(duì)象的引用可以注入,普通數(shù)據(jù)類型,集合等都可以在容器中進(jìn)行注入。

  1. 普通數(shù)據(jù)類型
  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. 引用數(shù)據(jù)類型
  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. 集合數(shù)據(jù)類型
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>

引入其他配置文件(分模塊開(kāi)發(fā))

實(shí)際開(kāi)發(fā)中,Spring的配置內(nèi)容非常多,這就導(dǎo)致Spring配置很繁雜且體積很大,所以,可以將部分配置拆解到其他配置文件中,而在Spring主配置文件通過(guò)import標(biāo)簽進(jìn)行加載

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

Spring配置數(shù)據(jù)源

1.1數(shù)據(jù)源(連接池)的作用【數(shù)據(jù)源(連接池)是提高程序性能如出現(xiàn)的?!?br> 常見(jiàn)的數(shù)據(jù)源(連接池):DBCP、C3P0、BoneCP Druid等

  • 事先實(shí)例化數(shù)據(jù)源
  • 初始化部分連接資源
  • 使用連接資源時(shí)從數(shù)據(jù)源中獲取
  • 使用完畢后將連接資源歸還給數(shù)據(jù)源
     <?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標(biāo)簽
       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標(biāo)簽
      >
      <!--獲取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>  =>通過(guò)spring標(biāo)簽獲取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數(shù)據(jù)源
     * @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>的配置

注解 說(shuō)明
@Component 使用在類上用于實(shí)例化bean
@Controller 使用在web層類上用于實(shí)例化bean
@Service 使用在service層類上用于實(shí)例化bean
@Repository 使用在dao層上用于實(shí)例化bean
@Autowired 使用在字段上用于根據(jù)類型依賴注入 【按照數(shù)據(jù)類型匹配對(duì)象 UserDao有多個(gè)實(shí)現(xiàn)類,需要加上下邊注解】
@Qualifier 結(jié)合@Autowired一起使用用于根據(jù)名稱依賴注入 按照id進(jìn)行匹配
@Resource 相當(dāng)于@Autowired+@Qualifier,按照名稱進(jìn)行注入
@Value 注入普通屬性 【 可以使用這個(gè)屬性使用spring表達(dá)式獲取xml文件中引入的jdbc.properties的內(nèi)容 @Value("${jdbc.driver}")賦值給標(biāo)注的屬性】
@Scope 標(biāo)注bean的作用范圍【單例/多列】
@PostConstruct 使用在方法上標(biāo)注該方法是bean的初始化方法
@preDestory 標(biāo)注該方法是bean的銷毀方法

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

//<bean id="userService" class="com.xjbt.service.impl.UserServiceImpl">
//@Component("userService")
@Service("userService") =>實(shí)例化serviceBean
@Scope(singleton【獲取之前創(chuàng)建一次對(duì)象】|prototype【獲取一次創(chuàng)建一次對(duì)象】)
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      =>初始化時(shí)自動(dòng)執(zhí)行該方法
public  public void init(){
}
@preDestory             =>容器銷毀時(shí)自動(dòng)執(zhí)行該方法
public  public void init(){
}
    public void save() {
        ApplicationContext app= new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userdao = app.getBean(UserDao.class);
        userdao.save();
    }
}

Spring新注解

注解 說(shuō)明
@Configuration 用于指定當(dāng)前類是一個(gè)Spring配置類,當(dāng)創(chuàng)建容器時(shí)會(huì)從該類上加載注解
@ComponentScan 用于指定Spring在初始化容器時(shí)要掃描的包。作用和在Spring 的xml配置文件中的<context:component-scan base-package="com.itheima"/>一樣
@Bean 使用在方法上,標(biāo)注將該方法的返回值存儲(chǔ)到Spring容器中
@PropertySource 用于加載.properties文件中的配置
@Import 用于導(dǎo)入其他配置類
@Configuration                                   =>標(biāo)志該類是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})                =>導(dǎo)入其他配置
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");=>根據(jù)配置文件中獲取對(duì)象
        ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfig.class);   =》根據(jù)配置類獲取對(duì)象

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應(yīng)用上下文獲取方式【了解】

如果有多個(gè)Servlet獲取Spring中創(chuàng)建的對(duì)象,就要寫多次 ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");來(lái)訪問(wèn)配置文件。顯然這樣是不合理的。

應(yīng)用上下文對(duì)象是通過(guò)方式獲取的,但是每次從容器中獲得Bean時(shí)都要編寫new ClasspathXmIApplicationContext(spring配置文件),這樣的弊端是配置文件加載多次,應(yīng)用上下文對(duì)象創(chuàng)建多次。

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項(xiàng)目中,可以使用ServletContextListener監(jiān)聽(tīng)Web應(yīng)用的啟動(dòng),我們可以在Web應(yīng)用啟動(dòng)時(shí),就加載Spring的配置文件,創(chuàng)建應(yīng)用上下文對(duì)象ApplicationContext,在將其存儲(chǔ)到最大的域servletContext域中,這樣就可以在任意位置從域中獲得應(yīng)用上下文ApplicationContext對(duì)象了。
1.web.xml中配置監(jiān)聽(tīng)對(duì)象。

 <!--配置監(jiān)聽(tīng)器-->
    <listener>
       <listener-class>com.xjbt.spring_MVC.listener.ContextLoaderListener</listener-class>
   </listener>

2,創(chuàng)建監(jiān)聽(tīng)對(duì)象:

public class ContextLoaderListener implements ServletContextListener {                       =>實(shí)現(xiàn)監(jiān)聽(tīng)接口
    public void contextInitialized(ServletContextEvent servletContextEvent) {                =>監(jiān)聽(tīng)服務(wù)啟動(dòng)時(shí)
        ServletContext servletContext = servletContextEvent.getServletContext();             =>獲取servletContext全局對(duì)象
        ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml"); =>獲取Spring容器對(duì)象以及在applicationContext.xml中配置的Bean對(duì)象
        servletContext.setAttribute("app",app)                                               =>將Spring容器存入Servlet全局對(duì)象 servletContext
    }
    public void contextDestroyed(ServletContextEvent servletContextEvent) {                  =>監(jiān)聽(tīng)服務(wù)結(jié)束時(shí)

    }
}

3servlet中獲取

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

優(yōu)化代碼

  1. 監(jiān)聽(tīng)對(duì)象
public class ContextLoaderListener implements ServletContextListener {                      
    public void contextInitialized(ServletContextEvent servletContextEvent) {                
        ServletContext servletContext = servletContextEvent.getServletContext();      
              
        ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");   
     ********存在問(wèn)題:如果修改了applicationContext.xml文件名稱,那么必須動(dòng)監(jiān)聽(tīng)對(duì)象********
        servletContext.setAttribute("app",app)                                               
    }
    public void contextDestroyed(ServletContextEvent servletContextEvent) {               
    }
}

解決辦法:
將applicationContext.xml配置到web.xml文件中

設(shè)置全局初始化參數(shù)
   <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>applicationContext.xml</param-value>
   </context-param>

修改監(jiān)聽(tīng)對(duì)象

public class ContextLoaderListener implements ServletContextListener {                      
    public void contextInitialized(ServletContextEvent servletContextEvent) {                
        ServletContext servletContext = servletContextEvent.getServletContext();  

       String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");    通過(guò)全局對(duì)象獲取全局初始化參數(shù)      
        
        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")  
   ********存在問(wèn)題:屬性名稱耦合度高********
        UserService userService = app.getBean(UserService.class);
        userService.save();
    }
}

解決辦法:創(chuàng)建工具

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是靜態(tài)方法,所以不用創(chuàng)建WebApplicationUtils對(duì)象直接使用這個(gè)方法。*****************
        UserService userService = app.getBean(UserService.class);
        userService.save();
    }
}

spring使用數(shù)據(jù)源

  1. 導(dǎo)包
 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
  1. web.xml配置
  <!--設(shè)置全局初始化參數(shù)-->
    <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

是通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。

AOP為Aspect Oriented Programming的縮寫,意思為面向切面編程,是通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。
AOP是OOP的延續(xù),是軟件開(kāi)發(fā)中的一個(gè)熱點(diǎn),也是Spring框架中的一個(gè)重要內(nèi)容,是函數(shù)式編程的一種衍生范型。利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開(kāi)發(fā)的效率。

  • 作用:在程序運(yùn)行期間,在不修改源碼的情況下對(duì)方法(save())進(jìn)行功能增強(qiáng)(日志控制)
  • 優(yōu)勢(shì):減少重復(fù)代碼,提高開(kāi)發(fā)效率,并且便于維護(hù)

    AOP的底層實(shí)現(xiàn)
    SpringMVC運(yùn)行期間生成代理對(duì)象,代理對(duì)象的方法先執(zhí)行增強(qiáng)功能,在調(diào)用目標(biāo)對(duì)象的方法,完成對(duì)象方法功能增強(qiáng)。

實(shí)際上,AOP的底層是通過(guò)Spring提供的的動(dòng)態(tài)代理技術(shù)實(shí)現(xiàn)的。在運(yùn)行期間,Spring通過(guò)動(dòng)態(tài)代理技術(shù)動(dòng)態(tài)的生成代理對(duì)象,代理對(duì)象方法執(zhí)行時(shí)進(jìn)行增強(qiáng)功能的介入,在去調(diào)用目標(biāo)對(duì)象的方法,從而完成功能的增強(qiáng)。

常用的動(dòng)態(tài)代理技術(shù)
JDK代理:基于接口的動(dòng)態(tài)代理技術(shù)
cglib代理:基于父類的動(dòng)態(tài)代理技術(shù)
如果目標(biāo)對(duì)象有接口:jdk通過(guò)目標(biāo)對(duì)象的接口動(dòng)態(tài)生成代理對(duì)象,
如果目標(biāo)對(duì)象沒(méi)有接口:通過(guò)cglib給目標(biāo)對(duì)象動(dòng)態(tài)生成一個(gè)子對(duì)象,這個(gè)對(duì)象就是代理對(duì)象。

  • jdk動(dòng)態(tài)代理:
public class AdviceTest {
    public static void main(String[] args) {
        //目標(biāo)對(duì)象
        final Target target=new Target();
        //增強(qiáng)對(duì)象
        final ProxyAdvice advice=new ProxyAdvice();

        //jdk動(dòng)態(tài)代理對(duì)象
        TargetInterface proxy=(TargetInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),//目標(biāo)對(duì)象類加載器
                target.getClass().getInterfaces(),//目標(biāo)對(duì)象相同的接口字節(jié)碼數(shù)組
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        advice.before();//增強(qiáng)方法
                        Object invoke=method.invoke(target,args);//執(zhí)行目標(biāo)方法   通過(guò)這個(gè)方法就目標(biāo)方法已被動(dòng)態(tài)代理
                        advice.after();//增強(qiáng)方法
                        return invoke;
                    }
                }
        );

        proxy.save();
    }
}
  • cglib動(dòng)態(tài)代理
public class AdviceTest {
    public static void main(String[] args) {
        //目標(biāo)對(duì)象
        final Target target=new Target();
        //增強(qiáng)對(duì)象
        final ProxyAdvice advice=new ProxyAdvice();

        //cglib動(dòng)態(tài)代理對(duì)象
        //1,創(chuàng)建增強(qiáng)器
        Enhancer enhancer=new Enhancer();
        //2,設(shè)置父類(目標(biāo)對(duì)象)
        enhancer.setSuperclass(target.getClass());
        //3,設(shè)置回調(diào)
        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,創(chuàng)建代理對(duì)象
        Target proxy =(Target) enhancer.create();
        proxy.save();
    }

Spring封裝的AOP

  1. AOP相關(guān)概念

Spring的AOP實(shí)現(xiàn)底層就是對(duì)上面的動(dòng)態(tài)代理的代碼進(jìn)行了封裝,封裝后我們只需要對(duì)需要關(guān)注的部分進(jìn)行代碼編寫,并通過(guò)配置的方式完成指定目標(biāo)的方法增強(qiáng)。

  1. 常用的術(shù)語(yǔ)如下:
    Target (目標(biāo)對(duì)象):代理的目標(biāo)對(duì)象
    Proxy(代理):一個(gè)類被AOP織入增強(qiáng)后,就產(chǎn)生一個(gè)結(jié)果代理類
    Joinpoint(連接點(diǎn)):所謂連接點(diǎn)是指那些被攔截到的點(diǎn)。在spring中,這些點(diǎn)指的是方法,因?yàn)閟pring只支持方法類型的連接點(diǎn)【目標(biāo)對(duì)象的方法】
    Pointcut(切入點(diǎn)):所謂切入點(diǎn)是指我們要對(duì)哪些Joinpoint進(jìn)行攔截的定義【目標(biāo)對(duì)象要增強(qiáng)的方法】
    Advice(通知/增強(qiáng))︰所謂通知是指攔截到Joinpoint之后所要做的事情就是【增強(qiáng)方法】
    Aspect(切面):是切入點(diǎn)和通知(引介)的結(jié)合【目標(biāo)方法加入增強(qiáng)方法】
    Weaving (織入):是指把增強(qiáng)應(yīng)用到目標(biāo)對(duì)象來(lái)創(chuàng)建新的代理對(duì)象的過(guò)程。spring采用動(dòng)態(tài)代理織入,而AspectJ采用編譯期織入和類裝載期織入
  2. 需要編寫的內(nèi)容
  • 編寫核心業(yè)務(wù)代碼(目標(biāo)類的目標(biāo)方法)
  • 編寫切面類,切面類中有通知(增強(qiáng)功能方法)
  • 在配置文件中,配置織入關(guān)系,即將哪些通知與哪些連接點(diǎn)進(jìn)行結(jié)合
  1. AOP技術(shù)實(shí)現(xiàn)的內(nèi)容
    spring框架監(jiān)測(cè)需要增強(qiáng)的方法【切入點(diǎn)】執(zhí)行時(shí),spring框架創(chuàng)建代理對(duì)象,對(duì)需要增強(qiáng)的方法進(jìn)行增強(qiáng),完成代碼運(yùn)行。

Spring框架監(jiān)控切入點(diǎn)方法的執(zhí)行。一旦監(jiān)控到切入點(diǎn)方法被運(yùn)行,使用代理機(jī)制,動(dòng)態(tài)創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象,根據(jù)通知類別,在代理對(duì)象的對(duì)應(yīng)位置,將通知對(duì)應(yīng)的功能織入,完成完整的代碼邏輯運(yùn)行。

  1. AOP底層使用哪種代理方式
    在spring中,框架會(huì)根據(jù)目標(biāo)類是否實(shí)現(xiàn)了接口來(lái)決定采用哪種動(dòng)態(tài)代理的方式。
快速入門基于application.xml
  1. 導(dǎo)入AOP相關(guān)坐標(biāo)
 <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. 創(chuàng)建目標(biāo)接口和目標(biāo)類(內(nèi)部有切點(diǎn))
  2. 創(chuàng)建切面類(內(nèi)部有增強(qiáng)方法)
public class MyAspect {
public void before(){
System. out.pr int1n("前置增強(qiáng)...... ....");
}

public void afterReturning() {
System. out.print1n("后置增強(qiáng)..........");
)
/ /Proceeding JoinPoint:正在執(zhí)行的連接點(diǎn)===切點(diǎn)
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System. out.print1n("環(huán)繞前增強(qiáng)....");
0bject proceed = pjp.proceed() ; / /切點(diǎn)方法System. out.println("環(huán)繞后增強(qiáng)....") ;return proceed;

public void afterThrowing(){
System. out.println("異常拋出增強(qiáng)......")
)
public void after(){
System. out.println("最終增強(qiáng).....");
)


  1. 將目標(biāo)類和切面類的對(duì)象創(chuàng)建權(quán)交給spring
  2. 在applicationContext.xml中配置織入關(guān)系
 <!--目標(biāo)對(duì)象-->
   <bean id="target" class="com.xjbt.proxy.aop.Target"></bean>
    <!--切面對(duì)象-->
    <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)包名.(*)類名.*(..)(任意參數(shù)的任意方法名)
method=> 
        </aop:aspect>
    </aop:config>

5.1抽取表達(dá)式

<aop:config>
        <!--聲明切面-->
        <aop:aspect ref="myAspect">
            <!--抽取切點(diǎn)表達(dá)式-->
            <aop:pointcut id="myPointcut" expression="execution(* com.xjbt.proxy.aop.*.*(..))"></aop:pointcut><!--切面:切點(diǎn)通知-->
            <aop:around method="after" pointcut-ref="myPointcut"/>
            <aop:after method="after" pointcut-ref="myPointcut" />
        </aop:aspect>
    </aop:config>
  1. 測(cè)試代碼
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AOPtest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1(){
        target.save();
    }
}
快速入門基于注解
  1. 創(chuàng)建目標(biāo)類(內(nèi)部有切點(diǎn))
@Component("target")
public class Target{
    public void save() {
        System.out.println("目標(biāo)方法執(zhí)行");
    }
}
  1. 創(chuàng)建切面類(內(nèi)部有增強(qiáng)方法)
@Component("myAspect")  //注入到spring中
@Aspect   //表示這是一個(gè)前面類
public class MyAspect {
    @Before("execution(* com.xjbt.proxy.anno.*.*(..))") //配置前置通知
    public void before(){
        System.out.println("動(dòng)態(tài)代理前置對(duì)象");
    }
}
  1. 將目標(biāo)類和切面類的對(duì)象創(chuàng)建權(quán)交給spring
  2. 在切面類中使用注解配置織入關(guān)系
  3. 在配置文件中開(kāi)啟組件掃描和AOP的自動(dòng)代理
<!--注解掃描包 識(shí)別放入spring容器的類創(chuàng)建對(duì)象-->
<context:component-scan base-package="com.xjbt.proxy.anno"/>
<!--使用aop注解 -->
<aop:aspectj-autoproxy />
  1. 測(cè)試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AOPtest {
    @Autowired
    private Target target;
    @Test
    public void test1(){
        target.save();
    }
}

spring事務(wù)控制

1. 編程式事務(wù)管理器相關(guān)對(duì)象
  • PlatformTransactionManager 平臺(tái)【Platform】事務(wù)控制器【TransactionManager】:spring的事務(wù)管理器
PlatformTransactionManager方法 說(shuō)明
Transactionstatus getTransaction (TransactionDefination defination) 獲取事務(wù)的狀態(tài)信息
void commit (Transactionstaxus status) 提交事務(wù)
void rollback(Transactionstatus status) 回滾事務(wù)

PlatformTransactionManager是接口類型,不同的 Dao層技術(shù)則有不同的實(shí)現(xiàn)類
例如:
Dao層技術(shù)是jdbc或mybatis 時(shí):org.springframework.jdbc.datasource.DatasourceTransactionManager
Dao層技術(shù)是hibernate時(shí):org.springframework.orm.hibernate5.HibernateTransactionManager

  • TransactionDefinition事務(wù)的定義信息對(duì)象:
方法 說(shuō)明
int getIsolationLevel() 獲得事務(wù)的隔離級(jí)別
int getPropogationBehavior() 獲得事務(wù)的傳播行為
int getTimeout() 獲得超時(shí)時(shí)間
boolean isReadonly() 是否只讀

1.事務(wù)隔離級(jí)別
設(shè)置隔離級(jí)別,可以解決事務(wù)并發(fā)產(chǎn)生的問(wèn)題,如臟讀、不可重復(fù)讀和虛讀。
ISOLATION_DEFAULT 【默認(rèn)】
ISOLATION_READ_UNCOMMITTED 【讀不可提交】
ISOLATION_READ_COMMITTED 【讀并提交】
ISOLATION_REPEATABLE_READ 【可重復(fù)讀】
ISOIATION_SERIALIZABLE 【串行化 全能解決性能底】

2.事務(wù)傳播行為【解決調(diào)用業(yè)務(wù)方法時(shí)事務(wù)統(tǒng)一性的問(wèn)題】
a業(yè)務(wù)方法調(diào)用b業(yè)務(wù)方法,都有事務(wù)控制,會(huì)出現(xiàn)重復(fù)或統(tǒng)一的問(wèn)題
REQUIRED:如果當(dāng)前沒(méi)有事務(wù),就新建一個(gè)事務(wù),如果已經(jīng)存在一個(gè)事務(wù)中,加入到這個(gè)事務(wù)中。一般的選擇(默認(rèn)值)
SUPPORTS:支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就以非事務(wù)方式執(zhí)行(沒(méi)有事務(wù))
MANDATORY:使用當(dāng)前的事務(wù),如果當(dāng)前沒(méi)有事務(wù),就拋出異常
REQUERS_NE新建事務(wù),如果當(dāng)前在事務(wù)中,把當(dāng)前事務(wù)掛起。
NOT SUPPORTED:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起
NEVER:以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),拋出異常
NESTED:如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒(méi)有事務(wù),則執(zhí)行REQUIRED類似的操作
超時(shí)時(shí)間:默認(rèn)值是-1,沒(méi)有超時(shí)限制。如果有,以秒為單位進(jìn)行設(shè)置
是否只讀:建議查詢時(shí)設(shè)置為只讀

  • TransactionStatus:事務(wù)具體的運(yùn)行狀態(tài)
    隨著程序的運(yùn)行實(shí)時(shí)變化的不需要配置。
方法 說(shuō)明
boolean hassavepoint() 是否存儲(chǔ)回滾點(diǎn)
boolean iscompleted() 事務(wù)是否完成
boolean isNewTransaction() 是否是新事務(wù)
boolean isRollbackonly() 事務(wù)是否回滾
2. 聲明式事務(wù)控制
  • spring配置文件中配置聲明

事務(wù)管理是屬于系統(tǒng)層面的服務(wù),而不是業(yè)務(wù)邏輯的一部分,如果想要改變事務(wù)管理策劃的話也只需要在定義文件中重新配置即可。
在不需要事務(wù)管理的時(shí)候,只要在設(shè)定文件上修改一下,即可移去事務(wù)管理服務(wù),無(wú)需改變代碼重新編譯,這樣維護(hù)起來(lái)極其方便。
注意: Spring聲明式事務(wù)控制底層就是AOP。

快速入門

  <!--配置切點(diǎn)-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>
    <!--配置事務(wù)管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置事務(wù)管理器 增強(qiáng)方法-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--設(shè)置事務(wù)屬性-->
        <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>

設(shè)置事務(wù)屬性的解釋

name:切點(diǎn)方法名稱
isolation:事務(wù)的隔離級(jí)別
propogation:事務(wù)的傳播行為
timeout:超時(shí)時(shí)間
read-only:是否只讀

  • spring注解事務(wù)控制
    自定義類可使用注解,非自定義的配置到xml中。
    xml配置文件
  <!--組件掃描-->

<context:component-scan base-package="com.itheima" />

    <!--配置事務(wù)管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--使用事務(wù)控制注解-->
<tx:annotation-driven transaction-manager="transactionManager" />

AccountService.java

@Service("accountService")
@Transactional(isolation = Isolation.DEFAULT)//所有類下的方法控制事務(wù)
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測(cè)試

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在需要進(jìn)行事務(wù)控制的類或是方法上修飾,注解可用的屬性同xml配置方式,例如:隔離級(jí)別、傳播行為等。
②注解使用在類上,那么該類下的所有方法都使用同一套注解參數(shù)配置。使用在方法上,不同的方法可以采用不同的事務(wù)參數(shù)配置。
③Xml配置文件中要開(kāi)啟事務(wù)的注解驅(qū)動(dòng)<tx : annotation-driven />

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容