ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SpringIOC和AOP

2022-06-24 21:05:20  阅读:116  来源: 互联网

标签:SpringIOC String Spring void public AOP println class


1.  Spring概述

1.1. 什么是Spring

Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架,由Rod Johnson在其著作 Expert One-On-One J2EE Development and Design 中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为J2EE应用程序开发提供集成的框架。Spring使用基本的 JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。 Spring的 核心是控制反转(IoC)和面向切面(AOP) 。简单来说, Spring是一个分层的JavaSE/EEfull-stack(一站式)轻量级开源框架。

EE开发分成三层结构

  • WEB层: Spring MVC
  • 业务层: Bean管理:(IOC)
  • 持久层: Spring的JDBC模板。ORM模板用于整合其他的持久层框架

Expert One-to-One J2EE Design and Development : J2EE 的设计和开发:(2002.EJB)Expert One-to-One J2EE Development without EJB : J2EE 不使用EJB的开发

1.2. 为什么学习Spring

  • 方便解耦,简化开发

Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理

  • AOP编程的支持

Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能

  • 声明式事务的支持

只需要通过配置就可以完成对事务的管理,而无需手动编程

  • 方便程序的测试

Spring对Junit4支持,可以通过注解方便的测试Spring程序

  • 方便集成各种优秀框架

Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持

  • 降低JavaEE API的使用难度

Spring对JavaEE开发中非常难用的一些API(如:JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低

1.3. Spring的版本

Spring3.X 和 Spring4.X 和 Spring5.X

1.4. spring功能模块划分

2.  JAVA类的耦合与解耦

2.1. 什么是程序的耦合

在开发中,可能会写很多的类,而有些类之间不可避免的产生依赖关系,这种依赖关系称之为耦合。
有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除的。

代码示例

public class CustomerServiceImpl implements CustomerService {               CustomerDao cusomerDao = new CustomerDaoImpl();}

以上的代码表示:业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。这种依赖关系就是我们可以通过优化代码解决的。

还有如下面的代码:

我们的类依赖了MySQL的具体驱动类,如果这时候因为某些原因数据库的品牌从MySQL改为Oracle,那么需要通过改源码来修改数据库驱动。这显然不是我们想要的。

public class JdbcDemo01{        public static void main(String[] args) throws Exception {               Class.forName("com.mysql.jdbc.Driver");               //new com.mysql.jdbc.Driver()        }}

2.2. 解决耦合的思路

当是我们学习JDBC时,是通过反射来注册驱动的,代码如下:

Class.forName("com.mysql.jdbc.Driver");

这时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除mysql的驱动jar包,依然可以编译。但是因为没有驱动类,所以不能运行。
不过,此处也有个问题,就是我们反射类对象的全限定类名字符串是在java类中写死的,一旦要改还是要修改源码。
解决这个问题也很简单,使用配置文件配置。

2.2.1.  工厂模式解耦

在实际开发中我们可以把所有的dao和service和controller对象使用配置文件配置起来,当启动服务器应用加载的时候,通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。

2.2.2.  控制反转-Inversion Of Control

上面解耦的思路有2个问题:
1、存哪去?
分析:由于我们是很多对象,肯定要找个集合来存。这时候有Map和List供选择。
到底选Map还是List就看我们有没有查找需求。有查找需求,选Map。
所以我们的答案就是
在应用加载时,创建一个Map,用于存放controller,Service和dao对象。
我们把这个map称之为容器。
2、还是没解释什么是工厂?
工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。
原来:
我们在获取对象时,都是采用new的方式。是主动的。

现在:
我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。

这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。
它的作用只有一个:削减计算机程序的耦合。

3.  Spring的 IOC

3.1. 什么是IoC

Inversion of Control:控制权的转移,创建对象的权利由应用程序转移到容器称为控制反转

3.2. IoC的作用

削减计算机程序的耦合(解除我们代码中的依赖关系)

3.3. 基于XML的IoC配置入门

创建Maven项目,resources 创建spring配置文件

pom.xml依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.8</version>
</dependency>

编写UserService类

public class UserService {
    public UserService() {
        System.out.println("UserService()......");
    }

    public void add() {
        System.out.println("UserService.add()......");
    }
}

编写applicationContext.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--告诉spring容器 创建UserService实例对象-->
   
<bean id="userService" class="com.tjetc.service.UserService"></bean>
</beans>

 

编写测试类Test

public class Test {
    public static void main(String[] args) {
        //实例化ClassPathXmlApplicationContext对象,配置文件作为参数
       
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //上线文对象 getBean方法获取配置bean对象UserService
       
UserService userService = context.getBean(UserService.class);
        //调用UserServiceadd方法
       
userService.add();
    }
}

结果:

3.4. ApplicationContext

ApplicatioContext接口有两个基本的实现类:

ClassPathXmlApplicationContext : 加载类路径下的Spring配置文件。

FileSystemXmlApplicationContext : 加载本地磁盘下的Spring配置文件。

4.   Bean的配置

4.1. spring中的bean元素相关配置Spring中的bean元素相关配置

配置文件中的bean元素用于描述需要Spring容器管理的对象。其 class属性 用于指被管理对象的完整类名。

4.1.1.  id属性和name属性

id属性 是为Spring容器管理的对象起个名字。其使用ID约束:

唯一

必须以字母开始

可以使用字母、数字、连字符、下划线、句号、冒号。但 不能出现特殊字符

name属性 也是为被Spring容器管理的对象起个名字。这样后续可以通过该名字从容器中获取对象。

没有ID中的那些约束(可以重复[不推荐]、可以出现特殊字符)

如果<bean>没有id的话,name可以当做id使用

举例

<bean id="bookAction"><bean name="/loginAction" >

 

此处的名称中由于有特殊字符,只能使用name属性。

结论:在定义bean的时候,推荐使用name属性 来为bean指定名称

4.1.2.  scope属性

用于声明bean在Spring容器中的作用范围。其取值包括:

singleton : 默认值 单例的;默认在Spring容器启动的时候就会创建该实例。

prototype : 多例的;默认在从Spring容器中获取bean时才会创建该实例。

request : WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中。

session : WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中。

globalSession : WEB项目中,应用在Porlet环境。如果没有Porlet环境,那么globalSession相当于session。

4.1.3.  生命周期属性

在bean被创建的时候,需要执行一些初始化的逻辑。可以指定bean中的一个方法为其初始化方法。这样Spring在创建完该对象之后,立即调用一下该初始化方法。

public void init() {
    System.out.println("UserService.init()......");
}

<bean id="userService" class="com.tjetc.service.UserService" init-method="init" ></bean>

 

在bean对象销毁的时候,需要执行一些销毁逻辑。可以指定bean中的一个方法为其销毁方法。这样Spring容器在关闭之前并且销毁该对象的时候,会调用一下该销毁方法。

public void destroy() {
    System.out.println("UserService.destroy().......");
}

 

<bean id="userService" class="com.tjetc.service.UserService" destroy-method="destroy"></bean>

 

注意,此处的销毁方法必须是在容器正常关闭(即执行close方法)时,才会被执行。

4.2. Spring生成Bean的时候的三种方式

方式1:无参数的构造方法的方式

<bean id="userService" class="com.tjetc.service.UserService"></bean>

方式2:静态工厂实例化的方式

<!--class 配置的是工厂类,factory-method配置工厂类的静态方法,让spring调用工厂类的静态方法产生对象并管理-->
<bean id="userService2" class="com.tjetc.factory.UserServiceFactory" factory-method="getBean"></bean>
public class UserServiceFactory {
    //静态工厂实例化的方式
    public static UserService getBean(){
        return new UserService();
    }
}

方式3:实例工厂实例化的方式

<!--
(1)配置创建UserServiceFactory2的工厂实例对象并管理
(2)配置调用UserServiceFactory2的工厂实例对象的getBean2的实例方法接收UserService对象并管理
-->
<bean id="userServiceFactory2" class="com.tjetc.factory.UserServiceFactory2"></bean>
<bean id="userService3" factory-bean="userServiceFactory2" factory-method="getBean2"></bean>
public class UserServiceFactory2 {
    /*实例方法,要调用次方法,必须先创建UserServiceFactory2的实例对象*/
    public UserService getBean2() {
        return new UserService();
    }
}

4.3. Spring中Bean属性的注入

4.3.1.  方式1:构造方法的方式注入属性

定义Car

public class Car {
    private String name;
    private int price;
    public Car(String name, int price) {
        this.name = name;
        this.price = price;
    }
    public String getName() { return name;}
    public void setName(String name) { this.name = name;}
    public int getPrice() { return price; }
    public void setPrice(int price) {this.price = price;}
    @Override
    public String toString() {
        return "Car{" + "name='" + name + '\'' + ", price=" + price +  '}';
    }
}

Spring配置Car

<!--spring根据Car的构造方法实例化对象-->
<bean id="car" class="com.tjetc.entity.Car">
    <constructor-arg name="name" value="carat"></constructor-arg>
    <constructor-arg name="price" value="17526"></constructor-arg>
</bean>

测试

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        Car car = (Car) context.getBean("car");
        System.out.println(car);
    }
}

4.3.2.  方式2:set方法的方式注入属性

基本类型的属性注入

定义Car

public class Car {
    private String name;
    private int price;
    public Car() {
    }
    public Car(String name, int price) {
        this.name = name;
        this.price = price;
    }
    public String getName() { return name;}
    public void setName(String name) { this.name = name;}
    public int getPrice() { return price; }
    public void setPrice(int price) {this.price = price;}
    @Override
    public String toString() {
        return "Car{" + "name='" + name + '\'' + ", price=" + price +  '}';
    }
}

Spring配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--基本类型属性注入 ,调用无参构造方法-->
    <bean id="car1" class="com.tjetc.entity.Car">
        <property name="name" value="车车车"></property>
        <property name="price" value="17526"></property>
    </bean>
</beans>

测试

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("applicationContext1.xml");
        Car car = (Car) context.getBean("car1");//根据配置的id 获取car对象
        System.out.println(car);
    }
}

对象类型的属性注入

定义Person

public class Person {
    private String name;
    private Car car;
    public String getName() {return name; }
    public void setName(String name) {this.name = name;}
    public Car getCar() { return car;}
    public void setCar(Car car) {this.car = car; }
    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", car=" + car + '}';
    }
}

Spring配置

<!--1、基本类型的属性注入
      调用无参构造方法
-->
<bean id="car1" class="com.tjetc.entity.Car">
    <!--使用属性设置值前提条件:对应的类的属性要有set方法-->
    <property name="name" value="车车车"></property>
    <property name="price" value="17526"></property>
</bean>
<!--2、复杂(对象)类型的属性注入-->
<bean id="person" class="com.tjetc.entity.Person">
    <property name="name" value="kelly"></property>
    <!--复杂类型,使用ref来引用已经配置的bean的id值-->
    <property name="car" ref="car1"></property>
</bean>

测试:

public class Test{
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
    //根据配置的id 获取Person对象
    Person person = (Person) context.getBean("person");
    System.out.println(person);
}

 

4.4. 在配置文件中引入Properties配置

方案1:配置Spring的Bean PropertySourcesPlaceholderConfigurer

<!--读取properties文件-->
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="locations">
        <array>
            <value>
                classpath*:db.properties
            </value>
        </array>
    </property>
</bean>
<!--配置DbConfiguration,使用属性注入读取到的properties.文件内容-->
<bean id="dbConfiguration" class="com.tjetc.common.DbConfiguration">
    <!--${}读取properties配置的key对应的值数据-->
    <!--使用properties配置内容  格式${key}   spEL-->
    <property name="driverName" value="${jdbc.driverName}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>

方案2:引入 context名称空间 。使用context中提供的 <context:property-placeholder> 指定它的 location 属性值,如果有多个文件,使用 逗号 分割。

<!--读取properties文件内容-->
<context:property-placeholder location="classpath*:db.properties"></context:property-placeholder>

 

<!--使用properties配置内容  格式${key}-->
<bean id="db" class="com.tjetc.entity.DbConfiguration">
    <property name="driverName" value="${driverName}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</bean>

classpath:xx 这种写法,只会搜索到文件夹类型的下面的资源。不会搜索到jar包中的。

classpath*:xx 这种写法,都会搜索到。所以【推荐用这种】

4.5. 复杂类型的注入

定义CollectionDemo

package com.tjetc.entity;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class CollectionDemo {
    private String[] strs;
    private List<String> list;
    private Map<String, String> map;
    private Properties properties;

    public String[] getStrs() {
        return strs;
    }

    public void setStrs(String[] strs) {
        this.strs = strs;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public Map<String, String> getMap() {
        return map;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "CollectionDemo{" +
                "strs=" + Arrays.toString(strs) +
                ", list=" + list +
                ", map=" + map +
                ", properties=" + properties +
                '}';
    }
}

Spring配置

<!--CollectionDemo的bean的配置-->
<!--复杂类型对象注入-->
<bean id="collectionDemo" class="com.tjetc.entity.CollectionDemo">
    <!--数组类型的属性注入-->
    <property name="strs">
        <array>
            <value>张三</value>
            <value>李四</value>
        </array>
    </property>
    <!--集合类型的属性注入-->
    <property name="list">
        <list>
            <value>jack</value>
            <value>lucy</value>
        </list>
    </property>
    <!--Map类型的属性注入-->
    <property name="map">
        <map>
            <entry key="username" value="aaa"></entry>
            <entry key="password" value="1111"></entry>
        </map>
    </property>
    <!--Properties的属性注入-->
    <property name="properties">
        <props>
            <prop key="mm">111</prop>
            <prop key="nn">222</prop>
        </props>
    </property>
</bean>

测试:

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
        CollectionDemo demo = (CollectionDemo) context.getBean("collectionDemo");
        System.out.println(demo);
}

4.6. Spring的分配置文件开发

随着项目越来越大,Spring的配置文件的内容也会越来越多。在实际的开发中,会将不同的配置定义在不同的xml文件中。有两种使用方式:

方式1:在创建容器的时候加载多个配置文件

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","propertiesContext.xml");

方式2:在主配置文件中包含其它配置文件(推荐使用)

<!-- 主配置文件applicationContext.xml中引入子配置文件 --><import resource="propertiesContext.xml"></import>

propertiesContext.xml
<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
    <!--读取properties配置文件-->
    <!--    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">-->
    <!--        <property name="locations">-->
    <!--            <array>-->
    <!--                <value>classpath*:db.properties</value>-->
    <!--            </array>-->
    <!--        </property>-->
    <!--    </bean>-->
    <!--<context:property-placeholder> 使用这个标签引入并读取properties文件-->
    <context:property-placeholder location="classpath*:db.properties"></context:property-placeholder>
</beans>

applicationContext.xml

<!--引入其他的配置文件,读取其他配置文件内容-->
<import resource="propertiesContext.xml"></import>
<!--配置DbConfiguration,使用属性注入读取到的properties文件内容-->
<bean id="dbConfiguration" class="com.tjetc.common.DbConfiguration">
    <!--${}读取properties配置的key对应的值数据-->
    <property name="driverName" value="${jdbc.driverName}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>

5.  Spring注解方式的Bean管理

主要解决的问题,在XML中一个一个对bean进行配置,开发效率也不高。

目的:

  • 自动发现哪些对象需要让Spring进行管理,自动将其注册到Spring容器中。
  • 自动注入bean依赖的内容。

5.1. Spring Bean管理中的常用注解

5.1.1.  准备工作

使用注解要先开启注解扫描的功能

<!--配置组件的扫描com.tjetc本包及其子孙包下的所有的在类上标注有@Controller,@Service,@Repository,@Component注解的类,
spring
会把标注了这些注解的类当做你配置bean节点一样纳入spring容器管理-->

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

5.1.2.  @Component

标注在类上,说明这是一个Spring组件。Spring中目前还提供了与 @Component 功能一致的其它三个衍生注解:

  • @Controller :WEB 层(控制层)
  • @Service :业务层
  • @Repository :持久层

衍生的三个注解是为了让标注类本身的用途清晰,Spring在后续版本会对其增强

作用和XML配置文件的<bean>标签的实现功能一致;

5.1.3.  属性注入的注解

使用注解注入的方式,可以不用提供set方法。

相关注解

  • @Value : 用于注入普通类型和String类型。
    • 加在成员变量上——原理是反射赋值(破坏封装)
    • 加在set方法上——原理是方法调用(推荐使用)
    • 它可以使用spring中的spEL(也就是spring的el表达式),SpEl的写法:${表达式}

@Value使用

package com.tjetc.common;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("adminConstant") //小括号字符串是AdminConstant类的bean的名称
public class AdminConstant {
    //@Value可以把配置数据注入到成员变量或者setXXX方法,前提条件:spring要对类实例化并管理
    //@Value 通过${} 读取key对应的value数据
    @Value("${img.base.path}")
    private String basePath;

    @Value("111111111")
    private String abc;

    private String baseType;

    @Value("${img.base.type}")
    public void setBaseType(String baseType) {
        this.baseType = baseType;
    }

    public String getBasePath() {
        return basePath;
    }

    public void setBasePath(String basePath) {
        this.basePath = basePath;
    }

    public String getBaseType() {
        return baseType;
    }

    public String getAbc() {
        return abc;
    }

    public void setAbc(String abc) {
        this.abc = abc;
    }

    @Override
    public String toString() {
        return "AdminConstant{" +
                "basePath='" + basePath + '\'' +
                ", abc='" + abc + '\'' +
                ", baseType='" + baseType + '\'' +
                '}';
    }
}

applicationContext.xml配置

<!--读取properties类型的配置文件-->
<context:property-placeholder location="classpath*:admin.properties"></context:property-placeholder>

测试

public class Test2 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        AdminConstant constant = (AdminConstant) context.getBean("constant");
        System.out.println(constant);
    }
}

 

 

  • @Autowired : 用于注入对象类型(自动装配)
    • 默认按类型进行装配
    • 按名称注入: @Qualifier——强制使用名称注入,一般不能单独使用和Autowired配合使用,当注入的对象类型有多个时,@Autowoired必须与@Qualifier一起使用

@Autowoired和@Qualifier使用

public class User {
    private Long id;
    public void setId(Long id) {
        this.id = id;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                '}';
    }
}

 

 

applicationContext.xml配置

<bean id="user1" class="com.tjetc.entity.User">
    <property name="id" value="1"></property>
</bean>
<bean id="user2" class="com.tjetc.entity.User">
    <property name="id" value="2"></property>
</bean>

PrintUser.java

@Component
public class PrintUser {
    @Autowired
    @Qualifier("user1")
    private User user;


    public void print(){
        System.out.println(user);
    }
}

 

测试

public class TestPrintUser {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        PrintUser printUser = (PrintUser) context.getBean("printUser");
        printUser.print();
    }
}

 

  • @Resource : 相当于@Autowired@Qualifier一起使用

@Component
public class PrintUser {
 
   
@Resource(name = "user2")
    private User user;

    public void print(){
        System.out.println(user);
    }
}

 

测试:

public class TestPrintUser {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        PrintUser printUser = (PrintUser) context.getBean("printUser");
        printUser.print();
    }
}

 

 

5.1.4.  Bean的作用范围的注解

注解@Scope

  • singleton : 单例
  • prototype : 多例

@Service
@Scope("prototype")
public class ProductServiceImpl {

   public ProductServiceImpl(){
    System.out.println("ProductServiceImpl()构造方法.....");
  }

}

测试

public class TestProductService {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        ProductServiceImpl productService1 = context.getBean(ProductServiceImpl.class);
        ProductServiceImpl productService2= context.getBean(ProductServiceImpl.class);
        System.out.println(productService1==productService2);
    }
}

 

5.1.5.  Bean的生命周期的配置

相关注解

  • @PostConstruct : 相当于init-method
  • @PreDestroy : 相当于destroy-method

 

@Service
public class ProductServiceImpl {
    public ProductServiceImpl(){
        System.out.println("ProductServiceImpl()构造方法.....");
    }

    @PostConstruct
    public void init(){
        System.out.println("ProductServiceImpl.init().....");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("ProductServiceImpl.destroy()......");
    }
}

测试

public class TestProductService {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        ProductServiceImpl productService = context.getBean(ProductServiceImpl.class);
        context.close();
   
}
}

6.  AOP的概述

Aop思想

6.1. 什么是AOP

Spring是解决实际开发中的一些问题:

  • AOP解决OOP中遇到的一些问题。是OOP的延续和扩展。
  • Spring引入AOP思想来为容器中管理的对象动态生成代理对象。

6.2. 为什么学习AOP

AOP的常用应用场景

  • 权限校验
  • 日志记录
  • 性能监控
  • 事务控制

6.3. Spring的AOP的由来

AOP最早由AOP联盟的组织提出的,制定了一套规范。Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范。

6.4. 底层实现

Spring的AOP底层用到两种代理机制

  • JDK的动态代理 : 针对实现了接口的类产生代理。
    • 被代理的类必须要实现接口,才能产生代理对象。如果没有接口将不能使用动态代理技术。
  • Cglib的动态代理 : 针对没有实现接口的类产生代理。应用的是底层的字节码增强的技术生成当前类的子类对象。
    • 可以对任何final声明的 类生成代理。

7.  Spring AOP基于XML实现

7.1. AOP开发中的相关术语

Joinpoint(连接点) : 所谓连接点是指 那些被拦截到的点 。在Spring中,这些点指的是方法。

Pointcut(切入点) : 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。

Advice(通知/增强) : 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知分为前置通知、后置 通知、异常通知、最终通知、环绕通知(切面要完成的功能)。

Aspect(切面) : 是切入点和通知(引介)的结合,即切入点+通知

7.2. 基于XML配置方式声明切面

7.2.1.  pom依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>

 

7.2.2.  写切面类


public class TransactionPrint {
    public void doBefore() {
        System.out.println("前置通知....");
    }

    public void doAfterReturning() {
        System.out.println("后置通知.....");
    }

    public void doAfterThrowing() {
        System.out.println("异常通知....");
    }

    public void doAfter() {
        System.out.println("最终通知.......");
    }

    public Object doRound(ProceedingJoinPoint pjp) throws Throwable {
        try {
            System.out.println("环绕前置通知.....");
            Object result = pjp.proceed();
            System.out.println("环绕后置通知.....");
            return result;
        } catch (Exception e) {
            System.out.println("环绕异常通知......");
            throw e;
        } finally {
            System.out.println("环绕最终通知....");
        }
    }
}

 

7.2.3.  被代理的业务类

public class PersonService {//目标类

 

public void add() {

System.out.println("PersonService.add()...");

}

public void update() {

System.out.println("PersonService.update()...");

}

public void del() {

System.out.println("PersonService.del()...");

throw new RuntimeException("出错了....");

}

}

7.2.4.  配置aop切面

<!--切面类和PersonServicespring生成实例对象并被管理起来-->
<bean id="xmlTransactionPrint" class="com.tjetc.common.XmlTransactionPrint"></bean>
<bean id="personService" class="com.tjetc.service.impl.PersonServiceImpl"></bean>
<!--aop配置-->
<aop:config>
    <!--切面配置-->
   
<aop:aspect id="myaop" ref="xmlTransactionPrint">
        <!--切点配置-->
        <!--
第一个*:代表所有返回值类型,包括有返回值和void-->
        <!--com.tjetc.service
:代表包名-->
        <!--
包名后面的两个点:代表本包或者当前包下子孙包-->
        <!--
第二个*:代表所有类-->
        <!--
第三个*:代表类下所有的方法-->
        <!--(..):
代表有0个或者1个或者多个参数-->
       
<aop:pointcut id="mycut" expression="execution(* com.tjetc.service..*.*(..))"/>
        <!--通知配置-->
       
<aop:before method="doBefore" pointcut-ref="mycut"/>
        <aop:after-returning method="doAfterReturning" pointcut-ref="mycut"/>
        <aop:after-throwing method="doAfterThrowing" pointcut-ref="mycut"/>
        <aop:after method="doAfter" pointcut-ref="mycut"></aop:after>

<!--<aop:around method="doRound" pointcut-ref="mycut"/>-->
    </aop:aspect>
</aop:config>

 

7.2.5.  测试类

public class TestXmlAop {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        PersonServiceImpl personService = context.getBean(PersonServiceImpl.class);
        personService.add();
        personService.update();
        personService.del();
    }
}

开启环绕通知配置,注释前置通知、后置通知、异常通知、最终通知,进行测试

8.  Spring AOP基于Annotation实现

8.1. 基于注解方式声明切面

8.1.1.  pom依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>

8.1.2.  启动@Aspect注解的支持和注解的扫描机制

applicationContext.xml配置文件用以下配置:

<!-- 开启对@Aspect注解的支持 -->

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

8.1.3.  写切面类

@Aspect声明的类,和普通类一样可以添加属性和方法,还可以包含切入点、通知、引入。

 Pointcut声明: 切入点声明包含两个部分: 

 1.签名:由一个名字和多个参数组成,必须返回void, 如:private void anyMethod(){}

 2.切入点表达式  如:@Pointcut("execution(* com.tjetc.service..*.*(..))")

@Component //把该类纳入到spring容器中管理
@Aspect //代表该类是一个切面类
public class AnnotationTransactionPrint {
    //声明切入点,切入点表达式(只对OrderServiceImpl这个类所有的方法进行aop
   
@Pointcut("execution(* com.tjetc.service.impl.OrderServiceImpl.*(..))")
    //切入点签名:由一个名字和多个参数组成,必须返回void
   
private void anyMethod() {
    }

    //前置通知,使用切入点签名
   
@Before("anyMethod()")
    public void doBefore() {
        System.out.println("前置通知....");
    }

    //后置通知,使用切入点签名
   
@AfterReturning("anyMethod()")
    public void doAfterReturning() {
        System.out.println("后置通知.....");
    }

    //异常通知,使用切入点签名
   
@AfterThrowing("anyMethod()")
    public void doAfterThrowing() {
        System.out.println("异常通知....");
    }

    //最终通知,使用切入点签名
   
@After("anyMethod()")
    public void doAfter() {
        System.out.println("最终通知.......");
    }

    //环绕通知,使用切入点签名
   
/*@Around("anyMethod()")*/
   
public Object doRound(ProceedingJoinPoint pjp) throws Throwable {
        try {
            System.out.println("环绕前置通知.....");
            Object result = pjp.proceed();
            System.out.println("环绕后置通知.....");
            return result;
        } catch (Exception e) {
            System.out.println("环绕异常通知......");
            throw e;
        } finally {
            System.out.println("环绕最终通知....");
        }
    }
}

 

 

8.1.4.  写一个业务类(被代理的类)

@Service //纳入spring的管理
public class OrderServiceImpl {
    public void add() {
        System.out.println("PersonService.add()...");
    }
    public void update() {
        System.out.println("PersonService.update()...");
    }
    public void del() {
        System.out.println("PersonService.del()...");
        throw new RuntimeException("出错了....");
    }
}

 

8.1.5.  测试类

public class TestAnnotationAop {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        OrderServiceImpl orderService = context.getBean(OrderServiceImpl.class);
        orderService.add();
        orderService.update();
        orderService.del();
    }
}

 

 

开启环绕通知配置,注释前置通知、后置通知、异常通知、最终通知,进行测试

 

标签:SpringIOC,String,Spring,void,public,AOP,println,class
来源: https://www.cnblogs.com/seventeen9588/p/16401291.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有