初学spring框架和小练习

Spring第一天

  1. Spring概述

1.1程序的耦合和解耦

      1.  什么是程序的耦合

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

/**

* 客户的业务层实现类依赖customerDao类,这两个类就称为耦合

*/

public class CustomerServiceImpl implements ICustomerService {

private ICustomerDao customerDao = new CustomerDaoImpl();

}

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

再比如:

下面的代码中,我们的类依赖了 MySQL 的具体驱动类,如果这时候更换了数据库品牌,我们需要改源码来修改数据库驱动。这显然不是我们想要的。

public class JdbcDemo1 {

/**

* JDBC 操作数据库的基本入门中存在什么问题?

* 导致驱动注册两次是个问题,但不是严重的。

* 严重的问题:是当前类和 mysql 的驱动类有很强的依赖关系。

* 当我们没有驱动类的时候,连编译都不让。

* 那这种依赖关系,就叫做程序的耦合

*

* 我们在开发中,理想的状态应该是:

* 我们应该尽力达到的:编译时不依赖,运行时才依赖。

*/

public static void main(String[] args) throws Exception {

//1.注册驱动

//DriverManager.registerDriver(new com.mysql.jdbc.Driver());

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

//2.获取连接

//3.获取预处理 sql 语句对象

//4.获取结果集

//5.遍历结果集

}

      1.  解决程序耦合的思路

当是我们讲解 jdbc 时,是通过反射来注册驱动的,代码如下:

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

这时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然可以编译。但是因为没有驱动类,所以不能运行。

不过,此处也有个问题,就是我们反射类对象的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改源码。

解决这个问题也很简单,使用配置文件配置。

      1. 工厂模式解耦

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

1.1.4 控制反转-Inversion Of Control

1 、存哪去?

分析:由于我们是很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。

到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。

所以我们的答案就是

在应用加载时,创建一个 Map,用于存放 action,Service 和 dao 对象。

我们把这个 map 称之为 容器。

2、还是没解释什么是工厂?

工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。

原来:

我们在获取对象时,都是采用 new 的方式。是主动的。

现在:

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

这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。

它的作用只有一个:削减计算机程序的耦合。

    1.  Spring介绍
      1. Spring 概述

Spring框架有两大核心 分别是IOC(控制反转)和AOP(面向切面编程)

1.2.1.1 Spring简介

Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoCInverse Of Control

反转控制)和 AOPAspect Oriented Programming:面向切面编程)为内核,提供了展现层 SpringMVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。

1.2.1.2 Spring的发展历程

1997 年 IBM 提出了 EJB 的思想

1998 年,SUN 制定开发标准规范 EJB1.0

1999 年,EJB1.1 发布

2001 年,EJB2.0 发布

2003 年,EJB2.1 发布

2006 年,EJB3.0 发布

Rod Johnson spring  之父)

Expert One-to-One J2EE Design and Development(2002)

阐述了 J2EE 使用 EJB 开发设计的优点及解决方案

Expert One-to-One J2EE Development without EJB(2004)

阐述了 J2EE 开发不使用 EJB 的解决方式(Spring 雏形)

      1.  Spring的优势

方便解耦,简化开发

通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

AOP  编程的支持

通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。

声明式事务的支持

可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。

方便程序的测试

可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

方便集成各种优秀框架

Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz 任务调度框架  等)的直接支持。低 降低 JavaEE API  的使用难度Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用  CXF 等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。

Java  源码是经典学习范例

Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。

 

 

 

      1.  Spring的体系结构

 

      1.  Spring开发包

官网:http://spring.io/

下载地址:

http://repo.springsource.org/libs-release-local/org/springframework/spring

解压:(Spring 目录结构:)

* docs  :API 和开发规范.

* libs  :jar 包和源码.

* schema :约束.

我们上课使用的版本是Spring 4.2.4

  1. 使用Spring的IOC解决程序耦合

2.1案例的前期准备

本章我们使用的案例是,客户的业务层和持久层的依赖关系解决。在开始 spring 的配置之前,我们要先准备一下环境。由于我们是使用 spring 解决依赖关系,并不是真正的要做增删改查操作,所以此时我们没必要写实体类。并且我们在此处使用的是 java 工程,不是 java web 工程。

2.1.1 创建业务层接口和实现类

客户接口: CustomerService

 

/**

* 客户的业务层接口

*/

public interface ICustomerService {

/**

* 保存客户

* @param customer

*/

void saveCustomer();

}

 

客户的实现类 CustomerServiceImpl:

/**

* 客户的业务层实现类

*/

public class CustomerServiceImpl implements ICustomerService {

private ICustomerDao customerDao = new CustomerDaoImpl();// 此处有依赖关系

@Override

public void saveCustomer() {

customerDao.saveCustomer();

}

}

 

2.1.2  创建持久层接口和实现类

客户的持久层接口:

public interface ICustomerDao {

/**

* 保存客户

*/

void saveCustomer();

}

 

 

 客户的持久层实现类:

 

public class CustomerDaoImpl implements ICustomerDao {

@Override

public void saveCustomer() {

System.out.println("保存了客户");

}

}

 

 

2.2 基于XML的配置

2.2.1 环境搭建

2.2.1.1 第一步: 拷贝必备的jar包到工程的lib目录

2.2.1.2 第二步:在类的根路径下创建一个任意名称的 xml  文件 (不能是中文)

给配置文件导入约束:

<?xml version="1.0" encoding="UTF-8"?>

<!-- 导入 schema

约束的位置在:

..\spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\html\xsd-configuration.html

文件中。注意:要导入 schema 约束

-->

<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">

</beans>

2.2.1.3  第三步:把资源交给 spring  来管理置 ,在配置文件中配置 service  和 dao

<!-- 把资源交给 spring 来管理 -->

<bean id="customerDao" class="com.bdqn.dao.impl.CustomerDaoImpl"/>

<bean id="customerService" class="com.bdqn.service.impl.CustomerServiceImpl"/>

2.2.2  测试环境是否成功

2.2.2.1  获取 spring  容器

/**

* 模拟一个表现层

*/

public class Client {

/**

* 获取 spring 的容器,以及从容器中获取 bean 对象

*

* ApplicationContext

* 它是一个接口,有两个实现类。

* 分别是:

* ClassPathXmlApplicationContext:

* 它永远都是从类的根路径下加载配置文件 推荐使用这种

* FileSystemXmlApplicationContext:

* 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

*/

public static void main(String[] args) {

//1.使用 ApplicationContext 接口,就是在获取 spring 容器

ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

//2.根据 bean 的 id 获取对象

ICustomerService  customerService  =  (ICustomerService)

ac.getBean("customerService");

System.out.println(customerService);

ICustomerDao customerDao = (ICustomerDao) ac.getBean("customerDao");

System.out.println(customerDao);

}

}

问题:由于对象都交给 spring 管理了,这时如果我们把 service 实现了的这行代码:

private ICustomerDao customerDao = new CustomerDaoImpl();// 此处有依赖关系

改为

private ICustomerDao customerDao = null;

使用 service 对象调用 saveCustomer 方法会出现什么问题呢?

运行结果:

空指针异常。

原因是 customerDao 对象没有赋值。

如果在程序运行期间,spring  框架能帮我们给 customerDao  对象赋了值,那该多好。

带着脑海里的这个想法,继续往下看。

2.2.2.2 Spring工厂类结构

BeanFactory 才是 Spring 容器中的顶层接口。ApplicationContext 是它的子接口。

BeanFactory 和 ApplicationContext 的区别:创建对象的时间点不一样。

ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。

BeanFactory:什么时候对象使用什么时候创建对象。

2.2.3 IOC 中 中 bean  标签 和 管理对象细节

2.2.3.1 bean  标签

作用:

用于配置对象让 spring 来创建的。

默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。

属性:

id :给对象在容器中提供一个唯一标识。用于获取对象。

class :指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。

scope :指定对象的作用范围。

* singleton :默认值,单例的.

* prototype :多例的.

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

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

init-method :指定类中的初始化方法名称。

destroy-method :指定类中销毁方法名称。

2.2.3.2 bean  的作用范围和生命周期

单例对象: scope="singleton"

一个应用只有一个对象的实例。它的作用范围就是整个引用。

生命周期:

对象出生:当应用加载,创建容器时,对象就被创建了。

对象活着:只要容器在,对象一直活着。

对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

多例对象: scope="prototype"

每次访问对象时,都会重新创建对象实例。

生命周期:

对象出生:当使用对象时,创建新的对象实例。

对象活着:只要对象在使用中,就一直活着。

对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。

2.2.4 spring  的依赖注入

2.2.4.1  依赖注入的概念

它是 spring 框架核心 ioc 的具体实现方式。简单的说,就是坐等框架把对象传入,而不用我们自己去获取.

2.2.4.2 构造函数注入

顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让 spring 框架来为我们注入。具体代码如下:

public class CustomerServiceImpl implements ICustomerService {

private String name;

private Integer age;

private Date birthday;

public CustomerServiceImpl(String name, Integer age, Date birthday) {

this.name = name;

this.age = age;

this.birthday = birthday;

}

@Override

public void saveCustomer() {

System.out.println(name+","+age+","+birthday);

}

}

<!-- 使用构造函数的方式,给 service 中的属性传值

要求:

类中需要提供一个对应参数列表的构造函数。

涉及的标签:

constructor-arg

属性:

index:指定参数在构造函数参数列表的索引位置

type:指定参数在构造函数中的数据类型

name:指定参数在构造函数中的名称 用这个找给谁赋值

=======上面三个都是找给谁赋值,下面两个指的是赋什么值的==============

value:它能赋的值是基本数据类型和 String 类型

ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean

-->

<bean id="customerService" class="com.bdqn.service.impl.CustomerServiceImpl">

<constructor-arg name="name" value=" 张三 "></constructor-arg>

<constructor-arg name="age" value="18"></constructor-arg>

<constructor-arg name="birthday" ref="now"></constructor-arg>

</bean>

<bean id="now" class="java.util.Date"></bean>

 

2.2.4.3 Set方法注入(重点)

顾名思义,就是在类中提供需要注入成员的 set 方法。具体代码如下:

public class CustomerServiceImpl implements ICustomerService {

private String name;

private Integer age;

private Date birthday;

public void setName(String name) {

this.name = name;

}

public void setAge(Integer age) {

this.age = age;

}

public void setBirthday(Date birthday) {

this.birthday = birthday;

}

@Override

public void saveCustomer() {

System.out.println(name+","+age+","+birthday);

}

}

<!-- 通过配置文件给 bean 中的属性传值:使用 set 方法的方式

涉及的标签:

property

属性:

name:找的是类中 set 方法后面的部分

ref:给属性赋值是其他 bean 类型的

value:给属性赋值是基本数据类型和 string 类型的

实际开发中,此种方式用的较多。

-->

<bean id="customerService" class="com.bdqn.service.impl.CustomerServiceImpl">

<property name="name" value="test"></property>

<property name="age" value="21"></property>

<property name="birthday" ref="now"></property>

</bean>

<bean id="now" class="java.util.Date"></bean>

     

Spring  第 二 天

第1 章 基于注解的 IOC  配置

1.1  写在最前

学习基于注解的 IoC 配置,大家脑海里首先得有一个认知,即注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。关于实际的开发中到底使用 xml 还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。

1.2  环境搭建

1.2.1  第一步:拷贝必备 jar 包到工程的 lib 目录。

注意:在基于注解的配置中,我们还要多拷贝一个 aop 的 jar 包。如下图:

1.2.2  第二步:在类的根路径下创建一个任意名称的 xml  文件 (不能是中文)

基于注解整合时,导入约束时需要多导入一个 context 名称空间下的约束.

 

给配置文件导入 context 约束:

 

 

<?xml version="1.0" encoding="UTF-8"?>

<?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

   http://www.springframework.org/schema/beans/spring-context.xsd">

  

</beans>

 

1.2.3 使用@Component 注解配置管理的资源

@Component(value="customerServie")

public class CustomerServiceImpl implements CustomerService {

   @Override

   public void saveCustomer(Customer customer) {

      System.out.println("执行了保存客户!");

   }

}

1.2.4  第四步在 spring  的配置文件中开启 spring  对注解 ioc 的支持

<!-- 告知spring框架在,读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中 -->

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

1.3 常用注解

1.3.1  用于创建对象的

相当于:<bean id="" class="">

1.3.1.1 @Component

作用:

把资源让 spring 来管理。相当于在 xml 中配置一个 bean。

属性:

value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首

字母小写。

1.3.1.2 @Controller @Service @Repository

他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。

他们只不过是提供了更加明确的语义化。

@Controller :一般用于表现层的注解。

@Service :一般用于业务层的注解。

@Repository :一般用于持久层的注解。

细节:如果注解中有且只有一个属性 要赋值时是 ,且名称是 value ,value可以省略 。

1.3.2  用于注入数据的

相当于:<property name="" ref=""> <property name="" value="">

1.3.2.1 @Autowired

作用:

自动按照类型注入。当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean

类型。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。

1.3.2.2 @Qualifier(不经常使用)

作用:

在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独

立使用,必须和@Autowire 一起使用;但是给方法参数注入时,可以独立使用。

属性:

value:指定 bean 的 id。

1.3.2.3 @Resource

作用:

直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。

属性:

name:指定 bean 的 id。

1.3.2.4 @Value

作用:

注入基本数据类型和 String 类型数据的

属性:

value:用于指定值

1.3.3  用于改变作用范围的:

相当于:<bean id="" class="" scope="">

1.3.3.1 @Scope

作用:

指定 bean 的作用范围。

属性:value:指定范围的值。

取值:singleton prototype request session

1.3.4  和生命周期相关的:( 了解)

相当于:<bean id="" class="" init-method="" destroy-method="" />

1.3.4.1 @PostConstruct

作用:

用于指定初始化方法。

1.3.4.2 @PreDestroy

作用:

用于指定销毁方法。

1.3.5  关于注解和 XML  的选择问题

注解的优势:

配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。

XML  的优势:

修改时,不用改源码。不涉及重新编译和部署

1.4  spring  管理对象细节

基于注解的 spring IoC 配置中,bean 对象的特点和基于 XML 配置是一模一样的。

写到此处,基于注解的 IoC 配置已经完成,但是大家都发现了一个问题:我们依然离不开 spring的 xml 配置文件,那么能不能不写这个 bean.xml,所有配置都用注解来实现呢?

答案是肯定的,请看下一章节。

1.5  spring  的纯注解配置

创建对象的注解:
@Component
@Controller(用于web层)
@Service(用于service)
@Repository(用于dao)
 主配置文件包扫描:
 <context:component-scan >
依赖注入:
@Autowired(自动按照类型注入)
@Qualifier(和@Autowired配合使用用于指定具体类型 value属性指定)
@Resource:jdk    name属性 用于指定具体类型
如果只有一个实现类: autowire和resource注解可以互换 

1.5.1  待改造的问题

我们发现,之所以我们现在离不开 xml 配置文件,是因为我们有一句很关键的配置:

<!-- 告知spring框架在,读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中 -->

<context:component-scan base-package="cn.bdqn"></context:component-scan>

如果他要也能用注解配置,那么我们就可以脱离 xml 文件了。

1.5.2 使用注解配置要扫描的包

项目结构图:

在此图中,我们已经一点也看不到 xml 的身影了。那么,那句关键的配置跑哪去了呢?

在一个新的类上:用这个类代替配置文件.

配置类代码:

@Configuration //标明该类是一个配置类

@ComponentScan(basePackages= {"com.bdqn"})//配置扫描的包

public class SpringConfiguration {

}

测试类代码:

@Test

   public void testConfig() {

//1.获取容器:由于我们已经没有了 xml 文件,所以再用读取 xml 方式就不能用了。

//这时需要指定加载哪个类上的注解

      ApplicationContext aContext=new AnnotationConfigApplicationContext(SpringConfiguration.class);

      //从容器中获得对象

      CustomerService customerService = (CustomerService) aContext.getBean("customerServie");

      Customer customer=new Customer();

      customer.setCustName("李斯");

      customer.setCustSource("互联网");

      //调用对象的保存用户的方法

      customerService.saveCustomer(customer);

   }

 

1.5.3  新注解说明

1.5.3.1 @Configuration

作用:

用于指定当前类是一个配置类,会从该类上加载注解。读取该类上@ ComponentScan 注解

初始化 spring 容器。

1.5.3.2 @ComponentScan

作用:

用于指定 spring 在初始化容器时要扫描的包。

属性:

basePackages :用于指定要扫描的包。和该注解中的 value 属性作用一样。

1.5.3.3 @Bean

作用:把方法的返回值放入到Spring容器中

该注解只能写在方法上,表明使用此方法创建一个对象,并且交给 spring 管理。

属性:name :给当前@Bean 注解的方法创建的对象指定一个名称(即 bean 的 id)。

配置类代码:

@Component

public class JdbcConfig {

   /**

   把方法的返回值放入Spring容器  name属性用于指定对象的名字 

   相当于 <bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource"></bean>

   */

   @Bean(name="ds")

   public DataSource getDs() throws Exception {

      //创建一个数据源

      ComboPooledDataSource dataSource=new ComboPooledDataSource();

      //设置数据源连接数据库的四要素

      dataSource.setDriverClass("com.mysql.jdbc.Driver");

      dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");

      dataSource.setUser("root");

      dataSource.setPassword("root");

      //返回数据源

      return dataSource;

   }

}

测试代码:

@Test

   public void testBean() {

      //根据配置类获得容器

      ApplicationContext aContext=new AnnotationConfigApplicationContext(SpringConfiguration.class);

      //从容器中获得数据源

      DataSource dSource=(DataSource) aContext.getBean("ds");

      //创建QueryRunner对象

      QueryRunner runner=new QueryRunner(dSource);

      //实行插入数据语句

      try {

         runner.update("insert into account (username,money) values('Monica',500000)");

      } catch (SQLException e) {

         e.printStackTrace();

      }

   }

1.5.3.4 @Import

作用:

用于导入其他配置类

属性:

value[] :用于指定其他配置类的字节码。

示例代码:这样就不需要再向容器中放入JdbcConfig类,@Component注解省略

直接使用@Important注解加载配置类

@Configuration //标明该类是一个配置类

@ComponentScan("com.bdqn")//配置扫描的包

@Import(JdbcConfig.class)//用于导入其它配置类

public class SpringConfiguration {

}

1.5.3.5 @PropertySource

作用:用于加载.properties 文件中的配置

属性:

value[] :用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:

public class JdbcConfig {

   @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(name="ds")

   public DataSource getDs() throws Exception {

      //创建一个数据源

      ComboPooledDataSource dataSource=new ComboPooledDataSource();

      //设置数据源连接数据库的四要素

      dataSource.setDriverClass(driver);

      dataSource.setJdbcUrl(url);

      dataSource.setUser(username);

      dataSource.setPassword(password);

      //返回数据源

      return dataSource;

   }

注意4.3以前版本需要提供占位符类:

我们目前上课使用的版本是 4.2.4, spring4.3  以前都需要提供一个占位符配置器:

PropertySourcesPlaceholderConfigurer

而在 spring4.3  以后,则不需要提供。

提供 的方式如下:(在 SpringConfiguration JdbcConfig 配置均可

4.2版本的配置

@Configuration //标明该类是一个配置类

@ComponentScan("com.bdqn")//配置扫描的包

@Import(JdbcConfig.class)//用于导入其它配置类

@PropertySource("classpath:/com/bdqn/config/jdbc.properties")

//导入properties配置文件 value属性用于指定配置文件的位置

public class SpringConfiguration {

   @Bean(name="pspc")

   public static PropertySourcesPlaceholderConfigurer createPSPC() {

      return new PropertySourcesPlaceholderConfigurer();

   }

}

 

1.5.3.6 @Qualifier的第二种使用方法

1.第一种方式  和@Autowire一起配合起来使用,用于指定固定一个对象的配置.

2.用于向方法参数注入Spring容器中对象.

示例代码:

@Bean(name="ds")

   public DataSource getDs() throws Exception {

      //创建一个数据源

      ComboPooledDataSource dataSource=new ComboPooledDataSource();

      //设置数据源连接数据库的四要素

      dataSource.setDriverClass(driver);

      dataSource.setJdbcUrl(url);

      dataSource.setUser(username);

      dataSource.setPassword(password);

      //返回数据源

      return dataSource;

   }

   /**

    * @Qualifier("ds") 直接使用容器中的ds 向形参赋值

    */

   @Bean(name="qr")

   public QueryRunner getRunner(@Qualifier("ds")DataSource ds) {

      return new QueryRunner(ds);

   }

测试代码:

   @Test

   public void testBean() {

      //根据配置类获得容器

      ApplicationContext aContext=new AnnotationConfigApplicationContext(SpringConfiguration.class);

      //创建QueryRunner对象

      QueryRunner runner=(QueryRunner) aContext.getBean("qr");

      //实行插入数据语句

      try {

         runner.update("insert into account (username,money) values('runner',100)");

      } catch (SQLException e) {

         e.printStackTrace();

      }

   }

第2章Spring整合Junit

2.1  准备测试环境

2.1.1 创建业务层接口和实现类

业务层接口:

public interface CustomerService {

   //保存用户的方法

   public void saveCustomer(Customer customer);

   //查询所有用户方法

   public List<Customer> getCustomers();

}

业务层实现类:

@Component("customerServie")

public class CustomerServiceImpl implements CustomerService {

   private CustomerDao customerDao;

   //提供set方法用于注入customerDao对象

   @Override

   public void saveCustomer(Customer customer) {

      customerDao.saveCustomer(customer);

   }

   public void setCustomerDao(CustomerDao customerDao) {

      this.customerDao = customerDao;

   }

   @Override

   public List<Customer> getCustomers() {

      return customerDao.getCustomers();

   }

}

 

2.1.2 创建持久层接口和实现类

持久层接口:

public interface CustomerDao {

   //保存用户的方法

   public void saveCustomer(Customer customer);

   //查询所有用户方法

   public List<Customer> getCustomers();

}

 

持久层实现类:

@Repository

public class CustomerDaoImpl implements CustomerDao {

   @Override

   public void saveCustomer(Customer customer) {

      System.out.println("保存了用户1111:"+customer);

   }

   @Override

   public List<Customer> getCustomers() {

      System.out.println("查询所用的客户!");

      return null;

   }

}

 

2.1.3  导入 junit 的 的 jar

2.1.4 编写测试类

public class CustomrServiceTest {

   //创建CustomerService

   private CustomerService customerService;

   @Test

   public void saveCustomer() {

      Customer customer=new Customer();

      customer.setCustName("阳江");

      customer.setCustSource("旅游海滨");

      customerService.saveCustomer(customer);

   }

   @Test

   public void findAllCustomer() {

      customerService.getCustomers();

   }

}

2.2  使用 xml  配置步骤

2.2.1 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">

   <bean id="service" class="com.bdqn.service.impl.CustomerServiceImpl">

        <property name="customerDao" ref="dao"></property>

   </bean>

   <bean id="dao" class="com.bdqn.dao.impl.CustomerDaoImpl"></bean>

</beans>

2.2.2  第一步:拷贝整合 junit  的必备 jar  包到 lib

此处需要注意的是,导入 jar 包时,需要导入一个 spring aop jar

2.2.3  第二步:使用@RunWith注解替换原有运行器(junit运行器)

//使用RunWith注解替换Spring的运行器

@RunWith(SpringJUnit4ClassRunner.class)

public class CustomrServiceTest {

   //创建CustomerService

   private CustomerService customerService;

   @Test

   public void saveCustomer() {

      Customer customer=new Customer();

      customer.setCustName("阳江");

      customer.setCustSource("旅游海滨");

      customerService.saveCustomer(customer);

   }

   @Test

   public void findAllCustomer() {

      customerService.getCustomers();

   }

}

 

2.2.4 第三步:使用@ContextConfiguration  指定 spring  配置文件的位置

//使用RunWith注解替换Spring的运行器

@RunWith(SpringJUnit4ClassRunner.class)

// @ContextConfiguration 用于指定配置文件的位置

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class CustomrServiceTest {

   //创建CustomerService

   private CustomerService customerService;

   @Test

   public void saveCustomer() {

      Customer customer=new Customer();

      customer.setCustName("阳江");

      customer.setCustSource("旅游海滨");

      customerService.saveCustomer(customer);

   }

   @Test

   public void findAllCustomer() {

      customerService.getCustomers();

   }

}

 

2.2.5  第四步使用@Autowired 给测试类中的变量注入数据

//使用RunWith注解替换Spring的运行器

@RunWith(SpringJUnit4ClassRunner.class)

// @ContextConfiguration 用于指定配置文件的位置

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class CustomrServiceTest {

   //创建CustomerService

   @Autowired

   private CustomerService customerService;

   @Test

   public void saveCustomer() {

      Customer customer=new Customer();

      customer.setCustName("阳江");

      customer.setCustSource("旅游海滨");

      customerService.saveCustomer(customer);

   }

   @Test

   public void findAllCustomer() {

      customerService.getCustomers();

   }

}

2.3  使用纯注解配置步骤

2.3.1  第一步:拷贝整合 junit的必备 jar 包到 lib  目录

此处需要注意的是,导入 jar 包时,需要导入一个 spring 中 aop 的 jar 包。

 

2.3.2  第二步 把资源都用注解管理

2.3.3  第三步:使用注解配置方式创建 spring

2.3.4 第四步:使用 RunWith 注解和 ContextConfiguration  注解配置

最终代码:

@Configuration

@ComponentScan("com.bdqn")

//使用RunWith注解替换Spring的运行器

@RunWith(SpringJUnit4ClassRunner.class)

// @ContextConfiguration 用于指定配置文件的位置

@ContextConfiguration(classes=CustomrServiceTest.class)

public class CustomrServiceTest {

   //创建CustomerService

   @Autowired

   private CustomerService customerService;

   @Test

   public void saveCustomer() {

      Customer customer=new Customer();

      customer.setCustName("阳江");

      customer.setCustSource("旅游海滨");

      customerService.saveCustomer(customer);

   }

   @Test

   public void findAllCustomer() {

      customerService.getCustomers();

   }

}

2.4  为什么 不把测试类配到 xml 

在解释这个问题之前,先解除大家的疑虑,配到 XML 中能不能用呢?

答案是肯定的,没问题,可以使用。

那么为什么不采用配置到 xml 中的方式呢?

这个原因是这样的:

第一:当我们在 xml 中配置了一个 bean,spring 加载配置文件创建容器时,就会创建对象。

第二:测试类只是我们在测试功能时使用,而在项目中它并不参与程序逻辑,也不会解决需求上的问题,所以创建完了,并没有使用。那么存在容器中就会造成资源的浪费。

所以,基于以上两点,我们不应该把测试配置到 xml 文件中。

总结:

今天的注解:

 

创建对象的注解:

@Component

@Controller(用于web层)

@Service(用于service)

@Repository(用于dao)

 主配置文件包扫描:

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

作用范围:

@Scope : singleton、prototype

 

依赖注入:

@Autowired(自动按照类型注入)

@Qualifier(和@Autowired配合使用用于指定具体类型 value属性指定)

@Resource:jdk    name属性 用于指定具体类型

如果只有一个实现类: autowire和resource注解可以互换

@Value

 

新注解:

@Configuration

@ComponentScan

@Bean       name属性 

@Import

@PropertySource

 

 

2.2.4.4  注入集合属性

顾名思义,就是给类中的集合成员传值,它用的也是 set 方法注入的方式,只不过变量的数据类型都是集合。我们这里介绍注入数组,List,Set,Map,Properties。具体代码如下:

public class CustomerServiceImpl implements ICustomerService {

private String[] myStrs;

private List<String> myList;

private Set<String> mySet;

private Map<String,String> myMap;

private Properties myProps;

public void setMyStrs(String[] myStrs) {

this.myStrs = myStrs;

}

public void setMyList(List<String> myList) {

this.myList = myList;

}

public void setMySet(Set<String> mySet) {

this.mySet = mySet;

}

public void setMyMap(Map<String, String> myMap) {

this.myMap = myMap;

}

public void setMyProps(Properties myProps) {

this.myProps = myProps;

}

@Override

public void saveCustomer() {

System.out.println(Arrays.toString(myStrs));

System.out.println(myList);

System.out.println(mySet);

System.out.println(myMap);

System.out.println(myProps);

}

}

<!-- 注入集合数据

List 结构的:

array,list,set

Map 结构的

map,entry,props,prop

-->

<bean id="customerService" class="com.bdqn.service.impl.CustomerServiceImpl">

<!-- 在注入集合数据时,只要结构相同,标签可以互换 -->

<!-- 给数组注入数据 -->

<property name="myStrs">

<set>

<value>AAA</value>

<value>BBB</value>

<value>CCC</value>

</set>

</property>

<!-- 注入 list 集合数据 -->

<property name="myList">

<array>

<value>AAA</value>

<value>BBB</value>

<value>CCC</value>

</array>

</property>

<!-- 注入 set 集合数据 -->

<property name="mySet">

<list>

<value>AAA</value>

<value>BBB</value>

<value>CCC</value>

</list>

</property>

<!-- 注入 Map 数据 -->

<property name="myMap">

<props>

<prop key="testA">aaa</prop>

<prop key="testB">bbb</prop>

</props>

</property>

<!-- 注入 properties 数据 -->

<property name="myProps">

<map>

<entry key="testA" value="aaa"></entry>

<entry key="testB">

<value>bbb</value>

</entry>

</map>

</property>

</bean>

第3 章 附录

3.1  Spring  配置文件中提示的配置

3.1.1 复制路径:

http://www.springframework.org/schema/beans/spring-beans.xsd

3.1.2 查找 XML Catalog:

 

 

 

       

Spring第三天

  1. AOP的相关概念
    1. AOP概述
      1. 什么是AOP

AOP:全称是 Aspect Oriented Programming 即:面向切面编程。

简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不

修改源码的基础上,对我们的已有方法进行增强。

      1. AOP的作用及优势

作用:

在程序运行期间,不修改源码对已有方法进行增强。

优势:

减少重复代码 提高开发效率 维护方便

      1. AOP的实现方式

使用动态代理技术

可能通过上面的介绍 ,我们还是没有一个清晰的 认识。没关系,我们看看下面的 具体应用。

    1. AOP的具体应用

1.2.1  案例中问题

这是我们之前在struts2课程中做的一个新增与全表查询的例子。下面是客户的业务层接口和实现类。

通过下面的代码,我们能看出什么问题吗?

CustomerDao接口:

public interface CustomerDao {

   //保存用户方法

   public void saveCustomer(Customer customer);

   //修改客户

   public void updateCustomer(Customer customer);

   //根据id删除客户

   public void deleteCustomerById(String customerId);

}

CustomerDao实现类:

   @Override

   public void saveCustomer(Customer customer) {

      Session session = HibernateUtils.getSession();

      //开启事务

      Transaction transaction = session.beginTransaction();

      //保存用户到数据库

      session.save(customer);

      //提交事务

      transaction.commit();

      //关闭资源连接

      session.close();

   }

   @Override

   public void updateCustomer(Customer customer) {

      Session session = HibernateUtils.getSession();

      //开启事务

      Transaction transaction = session.beginTransaction();

      //更新客户

      session.update(customer);;

      //提交事务

      transaction.commit();

      //关闭资源连接

      session.close();

     

   }

   @Override

   public void deleteCustomerById(String customerId) {

      Session session = HibernateUtils.getSession();

      //开启事务

      Transaction transaction = session.beginTransaction();

      Customer customer=new Customer();

      customer.setCustId(Long.parseLong(customerId));

      //更新客户

      session.delete(customer);;

      //提交事务

      transaction.commit();

      //关闭资源连接

      session.close();

     

   }

上面代码的问题就是: 我们的事务控制是重复性的代码。 这还只是一个业务类 里的两个方法 ,如果有 多个业务了,每个业务类中都会有这些重复性的代码。

思考:

我们有什么办法解决这个问题吗?

要想解决这个问题,我们得回顾一下之前我们讲过的知识: 动态代理

1.2.2 动态代理回顾

1.2.2.1 动态代理的特点

字节码随用随创建,随用随加载。

它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。

装饰者模式就是静态代理的一种体现。

1.2.2.2 动态代理常用的有两种方式

基于接口的动态代理

提供者:JDK 官方的 Proxy 类。

要求:被代理类 最少 实现一个接口。

 

基于子类的动态代理

提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。

要求:被代理类不能用 final 修饰的 类 (最终类)。

1.2.2.3 使用JDK官方Proxy类创建代理对象

此处我们使用的是一个演员的例子:

在一个村庄里,有个老人,会一门技艺,就是吹小号 ,村里有哪家办喜事,就请这位老人过来表演助助兴,老人表演完,收点钱。而随着时间的推移,这位老人有了一定的名气,请他表演的人越来越多了,他一个人接不了那么多活,他就想了一个办法,收徒弟,让徒弟去表演,他就作为一个中间人去联系表演。下面我们就用代码演示出来。

接口方法:

public interface IActor {

/**

 * 徒弟要求

 *  能够做基本表演和精彩表演

 */

 //基本表演

   public void basicAct(float money);

 //精彩表演

   public void wonderfulAct(float money);

}

实现类:

//实现了接口,就表示具有接口中的方法实现.:符合老人的要求

public class Actor implements IActor {

   @Override

   public void basicAct(float money) {

      System.out.println("拿到钱,开始基本表演:"+money);

   }

   @Override

   public void wonderfulAct(float money) {

      System.out.println("拿到钱,开始精彩表演:"+money);

   }

 

}

 

测试类:

public class Client {

   public static void main(String[] args) {

      //有一家人办喜事,找演员

      //创建一个演员 

      Actor actor=new Actor();

      /**

      * 代理:

      * 间接。

      * 获取代理对象:

      * 要求:

      * 被代理类最少实现一个接口

      * 创建的方式

      * Proxy.newProxyInstance(三个参数)

      * 参数含义:

      * ClassLoader:和被代理对象使用相同的类加载器。

      * Interfaces:和被代理对象具有相同的行为。实现相同的接口。

      * InvocationHandler:如何代理。

      * 策略模式:使用场景是:

      *    数据有了,目的明确。

      *    如何达成目标,就是策略。

      */

      IActor proxyActor=(IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(), new InvocationHandler() {

         @Override

         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            /**

             * 执行被代理对象的任何方法,都会经过该方法.

             * 此方法有拦截的功能

             * 参数:

             *  proxy: 代理对象的引用.不一定每次都用到

             *  method: 当前执行的方法对象

             *  args: 执行方法所需要的参数

             *  返回值:

             *  当前执行方法的返回值

             */

            //根据方法对象回到方法名

            String name = method.getName();

            //获得方法参数 

            Float money=(Float) args[0];

            //定义一个返回值

            Object rtValue=null;

            //每个演出的费用不一样,在这里开始判断

            if("basicAct".equals(name)) {

                //基本演出  少于2000不演

                if(money>2000) {

                   //看上去商家是给了 8000,实际到徒弟手里只有 4000

                   //这就是我们没有修改原来 basicAct 方法源码,对方法进行了增强

                   System.out.println("徒弟开始基本表演....");

                   rtValue=method.invoke(actor, money/2);

                }

            }

            if("wonderfulAct".equals(name)) {

                //精彩表演,没有5000不演

                if(money>5000) {

                   //看上去商家是给了 8000,实际到徒弟手里只有 4000

                   //这就是我们没有修改原来 basicAct 方法源码,对方法进行了增强

                   System.out.println("徒弟开始精彩表演....");

                   rtValue=method.invoke(actor, money/2);

                }

            }

            return rtValue;

         }

      });

      //没有徒弟的时候自己表演

      actor.basicAct(1000f);

      actor.wonderfulAct(5000f);

      System.out.println("-----------------------------");

      //有徒弟的时候,让师傅联系徒弟让徒弟表演

      proxyActor.basicAct(8000f);

      proxyActor.wonderfulAct(6000f);

   }

}

1.2.2.4 使用 CGLib 的 的 Enhancer

还是那个演员的例子,只不过不让他实现接口。

public class Actor {

   public void basicAct(float money) {

      System.out.println("拿到钱,开始基本表演:"+money);

   }

   public void wonderfulAct(float money) {

      System.out.println("拿到钱,开始精彩表演:"+money);

   }

 

}

public class Client {

   /**

   * 基于子类的动态代理

   * 要求:

   * 被代理对象不能是最终类

   * 用到的类:

   * Enhancer

   * 用到的方法:

   * create(Class, Callback)

   * 方法的参数:

   * Class:被代理对象的字节码

   * Callback:如何代理

   * @param args

   */

   public static void main(String[] args) {

   final Actor actor = new Actor();

   Actor cglibActor = (Actor) Enhancer.create(actor.getClass(),

   new MethodInterceptor() {

   /**

   * 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何

   方法进行增强。

   *

   * 参数:

   * 前三个和基于接口的动态代理是一样的。

   * MethodProxy:当前执行方法的代理对象。

   * 返回值:

   * 当前执行方法的返回值

   */

   @Override

   public Object intercept(Object proxy, Method method, Object[] args,

   MethodProxy methodProxy) throws Throwable {

   String name = method.getName();

   Float money = (Float) args[0];

   Object rtValue = null;

   if("basicAct".equals(name)){

   //基本演出

   if(money > 2000){

   rtValue = method.invoke(actor, money/2);

   }

   }

   if("wonderfulAct".equals(name)){

   //精彩演出

   if(money > 5000){

   rtValue = method.invoke(actor, money/2);

   }

   }

   return rtValue;

   }

   });

   cglibActor.basicAct(10000);

   cglibActor.wonderfulAct(100000);

   }

}

这个故事(示例)讲完之后,我们从中受到什么启发呢?它到底能应用在哪呢?

1.2.3 解决案例中的问题

思路只有一个:使用动态代理技术创建客户业务层的代理对象,在执行 CustomerDaoImpl 时,对里面的方法进行增强,加入事务的支持。

/**

* 用于创建客户业务层对象工厂(当然也可以创建其他业务层对象,只不过我们此处不做那么繁琐)

* @author Johnny.Chen

*

*/

public class BeanFactory {

/**

* 获取客户业务层对象的代理对象

* @return

*/

public static ICustomerDao getCustomerDao() {

//定义客户业务层对象

final ICustomerDao customerDao = new CustomerDaoImpl();

//生成它的代理对象

ICustomerDao proxyCustomerDao = (ICustomerDao)

Proxy.newProxyInstance(customerDao.getClass().getClassLoader()

,customerDao.getClass().getInterfaces(),

new InvocationHandler() {

//执行客户业务层任何方法,都会在此处被拦截,我们对那些方法增强,加入事务。

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws

Throwable {

String name = method.getName();

Object rtValue = null;

try{

//开启事务

HibernateUtil.beginTransaction();

//执行操作

rtValue = method.invoke(customerDao, args);

//提交事务

HibernateUtil.commit();

}catch(Exception e){

//回滚事务

HibernateUtil.rollback();

e.printStackTrace();

}finally{

//释放资源.hibernate 在我们事务操作(提交/回滚)之后,已经帮我们关了。

//如果他没关,我们在此处关

}

return rtValue;

}

});

return proxyCustomerDao;

}

}

 

 

    1. Spring中的AOP
      1. 关于代理的选择

在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

实现了接口使用JDK官方Proxy类创建代理对象,没有实现接口使用CGLib的Enhancer来创建代理对象.

      1. AOP相关术语

Joinpoint( 连接 点):

所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。

Pointcut( 切入点):

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

Advice( 通知/ 增强):

所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。

通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。

Introduction( 引介):

引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。

Target( 目标对象):

代理的目标对象

Weaving( 织入):

是指把增强应用到目标对象来创建新的代理对象的过程。

spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。

Proxy (代理):

一个类被 AOP 织入增强后,就产生一个结果代理类。

Aspect( 切面):

是切入点和通知(引介)的结合。

      1. 学习spring中的AOP要明确的事

a 、开发阶段(我们做的)

编写核心业务代码(开发主线):大部分程序员来做,要求熟悉业务需求。

把公用代码抽取出来,制作成通知。(开发阶段最后再做):AOP 编程人员来做。

在配置文件中,声明切入点与通知间的关系,即切面。:AOP 编程人员来做。

b 、运行阶段(Spring  框架完成的)

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

 

  1.  基于XML的AOP的配置

2.1 环境准备

2.1.1 准备客户的业务层接口和实现类

//业务层接口

public interface CustomerService {

   //保存客户

   public void saveCustomer();

   //修改客户

   public void updateCustomer(Integer id);

}

//业务层实现类

public class CustomerServiceImpl implements CustomerService {

   @Override

   public void saveCustomer() {

      System.out.println("调用持久层,执行保存客户!");

 

   }

   @Override

   public void updateCustomer(Integer id) {

      System.out.println("调用持久层,执行修改客户:"+id);

   }

}

2.2.1 拷贝必备的jar包到工程lib目录

2.1.3  第三步:创建 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"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

   http://www.springframework.org/schema/beans/spring-beans.xsd

   http://www.springframework.org/schema/aop

   http://www.springframework.org/schema/aop/spring-aop.xsd">

  

</beans>

2.1.4  第四步:把客户的业务 层配置到 spring  容器中

<!-- 把资源交给Spring来管理 -->

       <bean id="customerService" class="com.bdqn.service.impl.CustomerServiceImpl"></bean>

2.1.5  第五步:制作通知(增强的类)

package com.bdqn.log;

//一个记录日志的类

public class MyLog {

   public void printLog() {

      System.out.println("MyLog类中的pringLog方法开始记录日志了!!!");

   }

}

 

2.2  配置步骤

2.2.1  第一步:把通知类用 bean  标签配置起来

<!-- 把MyLog类交给Spring管理 -->

       <bean id="myLog" class="com.bdqn.log.MyLog"></bean>

2.2.2  第二步:使用 aop:config  声明 aop

   <!-- aop配置 -->

       <aop:config>

          <!-- 配置的代码写在此处 -->

       </aop:config>

2.2.3  第三步:使用 aop:aspect 配置切面

<!-- 配置切面 此标签要出现在 aop:config标签内部

              id 切面的唯一标识符

              ref 引用的是通知类的bean的id

           -->

          <aop:aspect id="logAdvice" ref="myLog">

            

          </aop:aspect>

2.2.4  第四步:使用 aop:before 配置前置通知

<!--

             method: 用于指定通知类的增强方法名

             pointcut-ref: 用于指定引用的切入点表达值

             -->

            <aop:before method="printLog" pointcut-ref="pt1"/>

 

2.2.5  第五步:使用 aop:pointcut 配置切入点表达式

<!--

            expression 切入点表达式

            id 切入点表达式的唯一标识符

             -->

            <aop:pointcut expression="execution(public void com.bdqn.service.impl.CustomerServiceImpl.saveCustomer())" id="pt1"/>

2.3  切入点表达式说明

    execution:

        匹配方法的执行(常用)

        execution(表达式)

        表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))

        写法说明:

        全匹配方式:

        public void cn.bdqn.service.impl.CustomerServiceImpl.saveCustomer()

        访问修饰符可以省略

        void cn.bdqn.service.impl.CustomerServiceImpl.saveCustomer()

        返回值可以使用*号,表示任意返回值

        * cn.bdqn.service.impl.CustomerServiceImpl.saveCustomer()

        包名可以使用*号,表示任意包,但是有几级包,需要写几个*

        * *.*.*.*.CustomerServiceImpl.saveCustomer()

        使用..来表示当前包,及其子包

        * cn..CustomerServiceImpl.saveCustomer()

        类名可以使用*号,表示任意类

        * cn..*.saveCustomer()

        方法名可以使用*号,表示任意方法

        * cn..*.*()

        参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数

        * cn..*.*(*)

        参数列表可以使用..表示有无参数均可,有参数可以是任意类型

        * cn..*.*(..)

        全通配方式:

        * *..*.*(..)

  

2.4  常用标签

2.4.1 <aop:config>

作用:

用于声明开始 aop 的配置

2.4.2 <aop:aspect>

作用:

用于配置切面。

属性:

id :给切面提供一个唯一标识。

ref :引用配置好的通知类 bean 的 id。

2.4.3 <aop:pointcut>

作用:

用于配置切入点表达式

属性:

expression :用于定义切入点表达式。

id :用于给切入点表达式提供一个唯一标识。

2.4.4 <aop:before>

作用:

用于配置前置通知

属性:

method :指定通知中方法的名称。

pointct :定义切入点表达式

pointcut-ref :指定切入点表达式的引用

2.4.5 <aop:after-returning>

作用:用于配置后置通知

属性:

method :指定通知中方法的名称。

pointct :定义切入点表达式

pointcut-ref :指定切入点表达式的引用

2.4.6 <aop:after-throwing>

作用:用于配置异常通知

属性:

method :指定通知中方法的名称。

pointct :定义切入点表达式

pointcut-ref :指定切入点表达式的引用

2.4.7 <aop:after>

作用:用于配置最终通知

属性:

method :指定通知中方法的名称。

pointct :定义切入点表达式

pointcut-ref :指定切入点表达式的引用

2.4.8 <aop:around>

作用:用于配置环绕通知

属性:

method :指定通知中方法的名称。

pointct :定义切入点表达式

pointcut-ref :指定切入点表达式的引用

2.5  通知的类型

2.5.1 类型说明

      <!-- 配置通知的类型

         aop:before

         用于配置前置通知。前置通知的执行时间点:切入点方法执行之前执行

         aop:after-returning

         用于配置后置通知。后置通知的执行时间点:切入点方法正常执行之后。它和异常通知只能有一个

         执行

         aop:after-throwing

         用于配置异常通知。异常通知的执行时间点:切入点方法执行产生异常后执行。它和后置通知只能执行一个。

         aop:after

         用于配置最终通知。最终通知的执行时间点:无论切入点方法执行时是否有异常,它都会在其后面执行。

         aop:around

         用于配置环绕通知。他和前面四个不一样,他不是用于指定通知方法何时执行的。

      -->

      <aop:before method="beforePrintLog" pointcut-ref="pt1"/>

      <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"/>

      <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"/>

      <aop:after method="afterPrintLog" pointcut-ref="pt1"/>

      <aop:around method="aroundPringLog" pointcut-ref="pt1"/>

2.5.2 环绕通知的特殊说明

/**

    * 环绕通知 它是 spring 框架为我们提供的一种可以在代码中手动控制增强部分什么时候执行的方式。 问题:

    * 当我们配置了环绕通知之后,增强的代码执行了,业务核心方法没有执行。 分析: 通过动态代理我们知道在 invoke

    * 方法中,有明确调用业务核心方法:method.invoke()。 我们配置的环绕通知中,没有明确调用业务核心方法。 解决: spring

    * 框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数 在环绕通知执行时,spring

    * 框架会为我们提供该接口的实现类对象,我们直接使用就行。 该接口中有一个方法 proceed(),此方法就相当于 method.invoke()

    */

   public void aroundPringLog(ProceedingJoinPoint pjp) {

      try {

         System.out.println("前置通知:Logger 类的 aroundPringLog 方法记录日志");

         pjp.proceed();

         System.out.println("后置通知:Logger 类的 aroundPringLog 方法记录日志");

      } catch (Throwable e) {

         System.out.println("异常通知:Logger 类的 aroundPringLog 方法记录日志");

         e.printStackTrace();

      } finally {

         System.out.println("最终通知:Logger 类的 aroundPringLog 方法记录日志");

      }

   }

  1.  基于注解的AOP配置

3.1 准备环境

3.1.1  第一步:准 备客户的业务层和接口并用注解配置 (需要增强的类)

//业务层接口

public interface CustomerService {

    //保存客户

    public void saveCustomer();

    //修改客户

    public void updateCustomer(Integer id);

}

//业务层实现类

public class CustomerServiceImpl implements CustomerService {

    @Override

    public void saveCustomer() {

        System.out.println("调用持久层,执行保存客户!");

    }

    @Override

    public void updateCustomer(Integer id) {

        System.out.println("调用持久层,执行修改客户:"+id);

    }

}

 

3.1.2  第二步:拷贝必备的 jar  包到工 程的 lib

3.1.3  第三步:创建 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"

    xmlns:aop="http://www.springframework.org/schema/aop"

    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/aop

   http://www.springframework.org/schema/aop/spring-aop.xsd

   http://www.springframework.org/schema/context

   http://www.springframework.org/schema/context/spring-context.xsd">

      

</beans>

 

3.1.4  第四步:把资源使用注解让 spring

@Service("customerService")

public class CustomerServiceImpl implements CustomerService {

   @Override

   public void saveCustomer() {

      //模拟异常

      int i=1/0;

      System.out.println("调用持久层,执行保存客户!");

   }

   @Override

   public void updateCustomer(Integer id) {

      System.out.println("调用持久层,执行修改客户:"+id);

   }

}

 

3.1.5  第五步:在配置文件中指定 spring

   <!-- 开启注解扫描  告诉Spring框架创建对象使用包扫描的方式 -->

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

3.2  配置步骤

3.2.1 第一步:把通知类也使用注解配置

//一个记录日志的类

@Component("logger")

public class MyLog {

   public void beforePrintLog() {

      System.out.println("前置通知:MyLog类中的pringLog方法开始记录日志了!!!");

   }

}

3.2.2  第二步:在通知类上使用@Aspect注解声明为切面

//一个记录日志的类

@Component("logger")

//表明当前类是一个切面类

@Aspect

public class MyLog {

   public void beforePrintLog() {

      System.out.println("前置通知:MyLog类中的pringLog方法开始记录日志了!!!");

   }

}

 

3.2.3  第三步:在增强的方法上使用@Before

//配置前置通知  value属性用于配置切入点表达式

   @Before("execution(* com.bdqn.service.impl..*.*(..))")

   public void beforePrintLog() {

      System.out.println("前置通知:MyLog类中的pringLog方法开始记录日志了!!!");

   }

3.2.4  第四步:在 spring 配置文件中开启 spring 对注解 AOP 的支持.

   <!-- 开启Spring对AOP的支持 -->

<aop:aspectj-autoproxy/>

 

3.3  常用注解

3.3.1 @Aspect :

作用:把当前类声明为切面类。

3.3.2 @Before :

作用:把当前方法看成是前置通知。

属性:

value:用于指定切入点表达式,还可以指定切入点表达式的引用。

3.3.3 @AfterReturning

作用:把当前方法看成是后置通知。

属性:

value:用于指定切入点表达式,还可以指定切入点表达式的引用。

3.3.4 @AfterThrowing

作用:把当前方法看成是异常通知。

属性:

value:用于指定切入点表达式,还可以指定切入点表达式的引用。

3.3.5 @After

作用:把当前方法看成是最终通知。

属性:

value:用于指定切入点表达式,还可以指定切入点表达式的引用。

3.3.6 @Around

作用:把当前方法看成是环绕通知。

属性:

value:用于指定切入点表达式,还可以指定切入点表达式的引用。

3.3.7 @Pointcut

作用:指定切入点表达式

属性:

value:指定表达式的内容

3.4  不使用 XML

@Configuration

@ComponentScan(basePackages="cn.bdqn")

@EnableAspectJAutoProxy

public class SpringConfiguration {

}

 

 

 

猜你喜欢

转载自blog.csdn.net/linshaolun0701/article/details/83149591