学习spring时记录的笔记

spring全家桶:
spring,springboot,springmvc,spring boot,spring cloud
spring:出现是在2002,解决企业开发的难度。
减轻对项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系。
spring核心技术 ioc和aop;
控制反转,和面向切面编程
能实现模块之间,类之间的解耦合。
把什么放入容器中:dao,service,controller类,工具类
不应该放到spring容器中的对象:实体类对象,实体类数据来自数据库
2.servlet,listener,filter等由tomcat容器管理。

依赖:classa种使用classb的属性或者方法,叫做classa依赖clssb
spring管理两者之间的关系
spring io

spring framework
Data Arcess
ORM-mybatis去访问数据库
spring webFliux异步框架

开发的时候使用三层架构:
第一层,界面层Controller 相当于servlet,指手画脚的大佬,对外提供访问的功能,将业务给业务逻辑层,就是创建对象,永远都有业务逻辑层的对象
第二层,业务逻辑层 Service 永远都有数据访问层的对象。
第三层:数据访问层 Dao

AOP:所有层都会有一些公共的操作,比如日志。日志就是一条输出语句,会一直伴随程序的开发,这个功能耦合在每个类的各个方法中都去做同样的处理,
我们会把这个功能脱离出来,谁要谁拿走,公共的代码就是切面。提出来,反射回去,底层就是动态代理。

spring :
特点:1.轻量
2.针对接口编程,解耦合
3.AOP编程的支持
4.方便继承各种优秀框架

框架怎么学:框架是一个软件,其他人写好的软件。
1)知道框架能做什么,mybatis–访问数据库,对表中的数据执行增删改查
2)框架的语法,框架要完成一个功能,需要一定的步骤支持。
3)框架的内部实现,框架内部怎么做,原理是什么。
4)通过学习,可以实现一个框架。

spring、的第一个核心功能:ioc 所有创建对象和属性赋值的功能全被spring夺走
控制反转:Inversion of Control
是一个乐论,概念,思想。
描述的:把对象的创建,赋值,管理工作都交给代码之外的容器实现,也就是对象的创建是有其他外部资源的。
控制:创建对象,对象的属性赋值,对象之间的关系管理。
反转:把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现,
由容器代替开发人员管理对象,创建对象,给属性赋值。

正转:由开发人员在代码中,使用new构造方法创建对象,由开发人员主动管理对象。
public static void main(String args[]){
student student=new student();
}
控制反转IoC(Inversion of Control)是一个概念,是一种思想。由Spring容器进行对象的创建和依赖注入.程序员在使用时直接取出使用.

正转:由程序员进行对象的创建和依赖注入称为正转.程序员说了算.
Student stu = new Student(); ===>程序员创建对象
stu.setName(“张三”); ===>程序员进行赋值
stu.setAge(22);

反转:由Spring容器创建对象和依赖注入称为反转,将控制权从程序员手中夺走,由给Spring容器,称为反转. 容器说了算.
===>Spring容器负责对象的创建
===>Spring容器依赖注入值

切记:Spring容器在启动时,就创建所有的对象stu…
容器:是一个服务器软件,一个框架(spring)
为什么要使用ioc:目的就是减少对代码的改动,也能实现不同的功能,解耦合。(在容器中创建对象)。

java中创建对象:
1.构造方法:new student();
2.反射
3.序列化
4.克隆
5.ioc:创建对象
6.动态代理
只有ioc不需要在自己的程序中写代码,其他都是需要自己去创建对象new studnt();

ioc的体现:
servlet 1.创建类继承HttpServlet
2.在web.XML注册servlet,使用
名称
3.没有创建对象Servlet对象,没有Myservlet myservlet=new MyServlet();
4.Servlet是Tomcat服务器它创建的。 tomcat也称为容器。
Tomcat作为容器,里面存放有Servlet对象,Listner,Filter对象。
我们在创建Servlet类的时候去调用HttpServlet或者其他Servlet的时候不要去创建对象,Tomcat已经帮我们创建好了对象

IOC的技术实现:
DI是ioc的技术实现:
DI:依赖注入(Dependency Injection),只需要在程序中提供要使用的对象名称就可以,
至于对象如何在容器中创建赋值,查找都由容器内部实现。
spring是使用的DI实现了IOC的功能,spring底层创建对象,使用的是反射机制。

spring是一个容器,管理对象,给属性赋值,底层是反射对象。

步骤实现:1.创建maven项目
使用mavenquicklystart创建的话需要自己新建resource,并指定文件类型。
2.加入maven的依赖
spring的依赖,版本5.2.版本

<!--添加spring核心包spring-context-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-project-info-reports-plugin</artifactId>
  <version>3.0.0</version>
</dependency>

插件:maven默认只会识别java文件,这个是识别其他文件


src/main/java

/*.xml
/.properties



src/main/resources

**/.xml
**/*.properties


junit依赖
3.创建类(接口和他的实现类)
和没有使用框架一样,就是普通的类。
public class student {
private String name;
private int age;
}

4.创建spring需要使用的配置文件
声明类的信息,这些类由spring创建和管理

<bean id="ditest01" class="hellospring.bao1.student">
    <!-- 调用student的set方法给属性赋值
    必须是""之中,因为这是xml的规则-->
5.测试spring创建的 //由spring容器对象创建 //如果想从spring容器中取出对象,先要创建容器对象,并启动才可以取出对象。

String config=“bao1/spring-context.xml”;
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
studnet result= (studnet) ac.getBean(“contextstudent”);
System.out.println(“student”+result);

<bean id="mydate" class="java.util.Date">
    <property name="time" value="12345"/>
</bean>

alter+insert创建包 和类等可以直接写xml搜到xml 创建spring。xml
alt加出车实现接口
shift+f6给文件重命名
ctrl+h可以看见实现类

FileSystemXmlApplicationContext(少用)
ClassPathXmlApplicationContext (多用)

正转的方式:
SomeService service=new SomeServiceImpl
service.dosome():

反转,配置beans.xml

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

<!-- 告诉spring创建对象
    声明bean 就是告诉spring要创建某个类的对象
id:对象的自定义名称,唯一值,spring通过对这个名称找到对象
class.类的全限定名(不能是接口,因为spring是反射机制创建对象,必须使用类-->
<!--
spring就完成了SomeeService someservice=new SomeServiceOmpl();的对象
spring把创建好的对象放在map中,spring框架有一个map存放对象的。
spingMap.put(id的值,对象);
例如:spring.put("someService",new SomeServliceImpl());
一个Bean声明一个对象
-->
************************************************************ DI:依赖注入 di的实现语法有两种: 1.在sring的配置文件中,使用标签和属性完成,叫做基于xml的di实现 2.使用spring中的注解,完成属性赋值,叫做基于注解的di实现

di的语法分类:
1.set注入(设值注入):spring调用类的set方法可以实现属性的赋值。
80%左右都是使用的set注入。

声明student对象
注入就是赋值的意思。
简单类型:spring 中规定java的基本数据类型和string就是简单数据类型
di:给属性赋值
i:set注入(设置注入):spring调用类的set方法,你可以在set放啊中完成属性赋值
1)简单数据类型的set注入









2)引用类型的set注入:spring调用类的set方法

<property name=“属性名称” ref=“bean的id(对象的名称)"/>






2.构造注入,spring调用类的有参数构方法,创建对象,在构造方法中完成赋值。
即在创建对象的同时,在构造方法中给属性赋值。
构造注入使用标签,一个表示构造方法的一个参数
标签属性:
name:表示构造方法的形参名。
index:表示构造方法的参数的位置,参数从左往右位置是0,1的顺序,按顺序时可以省略
ref:构造方法的形参类型是引用类型,使用ref。
!-- 构造注入–>










!–构造注入创建文件对象 -->



默认的顺序:
默认的构造方法的参数的顺序注入值





ctrl+shift+o用来清除包


给创建的对象赋值
A.使用setter注入
注入分为简单类型注入和引用类型注入
简单类型注入值使用value属性
引用类型注入值使用ref属性
必须要注意:使用setter注入必须提供无参的构造方法,必须提供setXXX()方法.

<!--创建学生对象-->
<bean id="stu" class="com.bjpowernode.pojo2.Student">
    <property name="name" value="李四"></property>    ===>简单类型注入
    <property name="age" value="22"></property>
    <property name="school" ref="school"></property>  ===>引用类型注入
</bean>
<!--创建学校对象-->
<bean id="school" class="com.bjpowernode.pojo2.School">
    <property name="name" value="清华大学"></property>
    <property name="address" value="海淀区"></property>
</bean>

B.使用构造方法注入
Student stu = new Student("张三",22);
   a.使用构造方法的参数名称进行注入值
        <bean id="school" class="com.bjpowernode.pojo3.School">
	        <constructor-arg name="name1" value="清华大学"></constructor-arg>
	        <constructor-arg name="address1" value="海淀区"></constructor-arg>
    	</bean>
   b.使用构造方法参数的下标注入值
		<bean id="stu" class="com.bjpowernode.pojo3.Student">
	        <constructor-arg index="0" value="钱七"></constructor-arg>
	        <constructor-arg index="1" value="22"></constructor-arg>
	        <constructor-arg index="2" ref="school"></constructor-arg>
	    </bean>
   c.使用默认的构造方法的参数的顺序注入值
   		<bean id="stuSequence" class="com.bjpowernode.pojo3.Student">
	        <constructor-arg value="陈十"></constructor-arg>
	        <constructor-arg value="22"></constructor-arg>
	        <constructor-arg ref="school"></constructor-arg>
	    </bean>

基于注解的IOC
也称为DI(Dependency Injection),它是IOC的具体实现的技术.

基于注解的IOC,必须要在Spring的核心配置文件中添加包扫描.@Component相当于皇冠就是一个标志,需要去告诉spring,这个包中有这个注解,我们就去创建对象。

<context:component-scan base-package=“com.bjpowernode.s01”></context:component-scan>

药: 创建对象并依赖注入
汤: xml 注解annotation
1)创建对象的注解
@Component:可以创建任意对象.创建的对象的默认名称是类名的驼峰命名法.也可以指定对象的名称@Component(“指定名称”).
@Controller:专门用来创建控制器的对象(Servlet),这种对象可以接收用户的请求,可以返回处理结果给客户端.
@Service:专门用来创建业务逻辑层的对象,负责向下访问数据访问层,处理完毕后的结果返回给界面层.
@Repository:专门用来创建数据访问层的对象,负责数据库中的增删改查所有操作.

案例:
@Component("stu")  //交给Spring去创建对象,就是在容器启动时创建
public class Student {
    @Value("张三")  ===>简单类型的值注入
    private String name;
    @Value("22")
    private int age;
    ...}

2)依赖注入的注解
简单类型(8种基本类型+String)的注入
@Value:用来给简单类型注入值

引用类型的注入
A.@Autowired:使用类型注入值,从整个Bean工厂中搜索同源类型的对象进行注入.
同源类型也可注入.
什么是同源类型:
  a.被注入的类型(Student中的school)与注入的类型是完全相同的类型
  b.被注入的类型(Student中的school父)与注入的类型(子)是父子类
  c.被注入的类型(Student中的school接口)与注入的类型(实现类)是接口和实现类的类型

注意:在有父子类的情况下,使用按类型注入,就意味着有多个可注入的对象.此时按照名称进行二次筛选,选中与被注入对象相同名称的对象进行注入.
  如果注入名称相同会先注入父类的对象,如果父类对象修改名字,就会注入子类的对象。
B.@Autowired
  @Qualifier("名称"):使用名称注入值,从整个Bean工厂中搜索相同名称的对象进行注入.

注意:如果有父子类的情况下,直接按名称进行注入值.
******************
添加包扫描的方式

1)单个包扫描(推荐使用)
<context:component-scan base-package=“com.bjpowernode.controller”></context:component-scan>
<context:component-scan base-package=“com.bjpowernode.service.impl”></context:component-scan>
<context:component-scan base-package=“com.bjpowernode.dao”></context:component-scan>
2)多个包扫描,多个包之间以逗号或空格或分号分隔
<context:component-scan base-package=“com.bjpowernode.controller com.bjpowernode.service ,com.bjpowernode.dao”></context:component-scan>
3)扫描根包(不推荐)
<context:component-scan base-package=“com.bjpowernode”></context:component-scan>
会降低容器启动的速度,导致多做无用功.

为应用指定多个 Spring 配置文件
当项目越来越大,需要多人合作开发,一个配置就存在很大隐患.
1)拆分配置文件的策略
A.按层拆
applicationContext_controller.xml


applicationContext_service.xml


applicationContext_mapper.xml

B.按功能拆
  applicationContext_users.xml        
    <bean id="uController" class="com.bjpowernode.controller.UsersController">
    <bean id="uService" class="com.bjpowernode.controller.UsersService">
    <bean id="uMapper" class="com.bjpowernode.controller.UsersMapper">
  applicationContext_book.xml      
    <bean id="bController" class="com.bjpowernode.controller.BookController">
    <bean id="bService" class="com.bjpowernode.controller.BookService">
    <bean id="bMapper" class="com.bjpowernode.controller.BookMapper">

spring配置文件的整合
1)单个文件导入



2)批量导入

junit:单元测试:一个工具类库,做测试方法使用的。
单眼:指定的是方法,一个类中有很多方法,一个方法称为单元。
使用单元测试:
加入依赖junit
src/test/java目录中创建类
3.创建public方法
1)public方法
2)方法名称自定义,建议名称是test+你要测试方法的名称
3)
this 表示当前对象。this.name=name语句表示一个赋值语句,等号左边的 this.name 是指当前对象具有的变量 name,
等号右边的 name 表示参数传递过来的数值。

ctrl+f12查看类的结构

引用类型的自动注入。

<!--
<bean>的id名称相同,且数据类型是已知的,这样的容器中bean,spring能够赋值给引用类型。
-->

byTrype

<bean id="autostudent2" class="hellospring.bao1.student3" autowire="byType">
    <property name="name" value="wangba"/>
    <property name="age" value="13"/>
</bean>
<!-- 被调用的bean只能有一个,不然不知道调用谁-->
<!--<bean id="school3" class="hellospring.bao1.school2">-->
    <!--<property name="name" value="广科"/>-->
    <!--<property name="local" value="东莞"/>-->
<!--</bean>-->

   <bean id="autostudent3" class="hellospring.bao1.student3" autowire="byType">
    <property name="name" value="十一"/>
    <property name="age" value="11"/>
</bean>
<bean id="frist" class="hellospring.bao1.firstschool">
    <property name="name" value="广州"/>
    <property name="local" value="优依恋"/>
</bean>
<bean id="student4" class="hellospring.bao1.student4" autowire="byType">

</bean>
<bean id="studentservliceImpl" class="hellospring.bao1.studentServiceImpl">
1.首先,区分清楚什么是byType,什么是byName。


比如说如上这段代码,byName就是通过Bean的id或者name,byType就是按Bean的Class的类型。
若autowire="byType"意思是通过 class="cn.com.bochy.dao.impl.UserDaoImpl"来查找UserDaoImpl下所有的对象。
代码autowire="byName"意思是通过id="userDao"来查找Bean中的userDao对象

建议看 《Spring in Action》 第三章第一节,“自动装配 Bean 属性”

在spring中@Autowired注入规则:
1.@Autowired默认是按照byType进行注入的,但是当byType方式找到多个符合

二.spring注入的基本语法如下:
xml中语法如下:
如果是属性注入,需要为每一个依赖类创建相应的getter和setter方法。
如果是构造方法注入,需要为依赖类创建相应的构造方法。
属性注入的语法如下:




如例子:

因为UserService依赖于UserDao,因此需要在userService中创建相应的getter和setter方法。

多个配置优势*
1.每个文件的大小比一个文件要小很多,效率高。
2.避免多人竞争带来的冲突。
如果你的项目有多个模块(相关的功能在一起),一个模块一个配置文件。
学生考勤模块一个配置文件
、学生成绩一个配置文件。
多文件的分配方式:
1.按功能模块,一个模块一个配置文件。
2.按类的功能,数据库相关的配置一个文件,做事务的功能一个配置文件,做service功能的一个配置文件等。

为应用指定多个 Spring 配置文件
在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变
得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将
Spring 配置文件分解成多个配置文件。
包含关系的配置文件:
多个配置文件中有一个总文件,总配置文件将各其它子文件通过引入。在 Java
代码中只需要使用总配置文件对容器进行初始化即可。
Spring 配置文件:
也可使用通配符*。但,此时要求父配置文件名不能满足所能匹配的格式,否则将出现
循环递归包含。就本例而言,父配置文件不能匹配 spring-
.xml 的格式,即不能起名为
spring-total.xml。

spring-total.xml表示主配置文件:包含其他的配置文件的,主配置文件一般是不能定义对象的。
语法:
关键字:"classpath"表示类路径(class文件所在的目录),在spring的配置文件中要指定
其他文件的位置,需要使用classpath,告诉spring到哪去加载读取文件。


classpath表示在classes文件目录下查找

String config="bao1/spring-total.xml";
 <import resource="classpath:spring-*.xml"/>
 使用这个方式的时候,spring-total不能使用必须改名。

*******************注解

 @注解也是一个类

1。基于注解的di:通过注解完成java对象创建,属性赋值。
1)使用注解的步骤:
1.加入maven的依赖 spring-context,在你加入spring-context的同时,间接加入spring-aop的依赖。
使用注解不许使用spring-aop依赖。
context 理解为上下文最为合适。
自动将所有“基本”模块(如context,core,aop,beans,expression)添加到我的项目中
即添加子依赖的时候会自动将父依赖添加到项目中,防止出错。

通过自动包含传递依赖项,Maven避免了发现和指定您自己的依赖项所需的库的需要。

此特性通过从指定的远程存储库读取依赖项的项目文件来实现。通常,这些项目的所有依赖项都在项目中使用,项目继承的任何依赖项都是从父项目继承的,或者是从依赖项继承的,等等。

可以从依赖项收集的级别数没有限制。只有在发现循环依赖关系时才会出现问题。

通过传递依赖关系,所包含的库的图形可以快速增长相当大。因此,还有其他特性限制了哪些依赖项包括在内:

依赖中介-这决定了当遇到多个版本作为依赖项时,将选择哪个版本的工件。
Maven选择“最近的定义”。也就是说,它使用依赖树中与项目最接近的依赖项的版本。
始终可以通过在项目的POM中显式声明版本来保证版本。
请注意,如果两个依赖关系版本在依赖树中处于相同的深度,则第一个声明将获胜。
spring context 包括什么
主要包括:

DefaultListableBeanFactory
这就是大家常说的 ioc 容器,它里面有很多 map、list。spring 帮我们创建的 singleton 类型的 bean 就存放在其中一个 map 中。我们定义的监听器(ApplicationListener)也被放到一个 Set 集合中。
BeanDefinitionRegistry
把一个 BeanDefinition 放到 beanDefinitionMap。
AnnotatedBeanDefinitionReader
针对 AnnotationConfigApplicationContext 而言。一个 BeanDefinition 读取器。
扩展点集合
存放 spring 扩展点(主要是 BeanFactoryPostProcessor、BeanPostProcessor)接口的 list 集合。
4. spring context 的生命周期
下面大家可以结合代码这段代码去理解 spring context 的生命周期。

public static void main(String[] args) {
// 初始化和启动
AnnotationConfigApplicationContext acaContext = new AnnotationConfigApplicationContext(AppConfig.class);
// 运行
acaContext.getBean(ServiceA.class);
// 关闭/销毁
acaContext.close();
}
4.1 初始化和启动
我们平时常说的spring 启动其实就是调用 AbstractApplicationContext#refresh
完成 spring context 的初始化和启动过程。spring context 初始化从开始到最后结束以及启动,
这整个过程都在 refresh 这个方法中。refresh 方法刚开始做的是一些 spring context 的准备工作
,也就是 spring context 的初始化,比如:创建 BeanFactory、注册 BeanFactoryPostProcessor 等,
只有等这些准备工作做好以后才去开始 spring context 的启动。

与现实生活联系一下,你可以把初始化理解为准备原料(对应到编程中就是创建好一些数据结构,并
为这些数据结构填充点数据进去),等准备了你才能去真正造玩偶、造东西呀(对应到编程中就是执行算
法)。在编程中数据结构与算法是分不开的也是这个道理呀,它们相互依赖并没有严格的界限划分。

4.2 运行
spring context 启动后可以提供它的服务的这段时间。

4.3 关闭/销毁
不需要用 spring context ,关闭它时,其实对应到代码上就是 acaContext.close();
org.springframework
spring-core
4.0.4.RELEASE

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.5.RELEASE</version>
</dependency>
加入spring依赖会间接加入许多别的依赖。 2)在类中加入sprng的注解(多个不同功能的注解) 3)在spring 的配置文件中,加入一个组件扫描的标签,说明注解在你的项目中的位置。

学习的注解:
1.@Component
2.@Respotory
3.@Service
4.@Controller
5.@Value
6.@Autowired
7.@Resource

*******通过spring的注解完成java对象的创建,属性,代替xml文件
实现步骤:
1.加入依赖
2.创建类,在类中加入注释
3.创建spring的配置文件
声明组件扫描器的标签,知名注解在你的项目中的位置。

/*
@Compoent 创建对象的,等同于的功能
属性:value就是对象的名称,也就是bean的id值
value的值是唯一的,创建的对象在整个spring容器中就一个
位置是在类的上面,表示创建这个类的对象
@Component(value = “mystudent”)等同于
//spring中和Component功能一致的,创建对象的注解还有:
1.@Repositroy :放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的
(用在持久层类的上面)
2.@Service(用在业务层上面)用在service实现类上面,创建service对象
,service对象是做业务处理,可以由事务等功能的。
3.@Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,控制器
对象,能够接受用户提交的参数,显示请求的处理结果。
以上三个注解的使用语法和Conmponet一样,都能够创建对象,但是这三个注解还有额外的功能。
给类赋予不同的角色,给项目分层。
不确定这个类属于什么的时候用component

*/
@Component(value = “contextstudent”)
//注解value可以省略@Component(“contextstudent”)
//可以由spring提供名字@Component,这个名字是类名的首字母小写

<!--
加入component-scan标签,配置文件的变化
1.加入一个新的约束文件spring-context.xsd
2.给这个心的约束文件起个命名空间的名称
-->
<context:component-scan base-package="hellospring.bao2"/>

<!-- 指定多个包的三种方式:
   <context:component-scan base-package="hellospring.bao1"/>
      <context:component-scan base-package="hellospring.bao2"/>
      第二种:
      使用分隔符(;或,)分隔多个包名
        <context:component-scan base-package="hellospring.bao1;hellopsprig.bao2"/>
        第三种:指定父包:
                 <context:component-scan base-package="hellospring"/>

JavaBean是描述Java的软件组件模型,有点类似于Microsoft的COM组件概念。
在Java模型中,通过JavaBean可以无限扩充Java程序的功能,通过JavaBean的组合可以快速的生成新的应用程序。
对于程序员来说,最好的一点就是JavaBean可以实现代码的重复利用,另外对于程序的易维护性等等也有很重大的意义。
比如说一个购物车程序,要实现购物车中添加一件商品这样的功能
,就可以写一个购物车操作的JavaBean,建立一个public的AddItem成员方法
,前台Jsp文件里面直接调用这个方法来实现。如果后来又考虑添加商品的时候需要判断库存是否有货物,没
有货物不得购买,在这个时候我们就可以直接修改JavaBean的AddItem方法,加入处理语句来实现,这样就完
全不用修改前台jsp程序了。

给简单类型赋值
@value;简单类型的属性赋值。
属性:value 是String类型的,表示简单类型的属性值。
位置:1.在属性定义的上面,无需set方法,推荐使用。
2.在set方法上面。
使用的是反射@Value(value=“”)
@Value(“zhangsan”)
private String name;
@Value(“18”)
private String age;
/

引用类型的赋值
@Autowired spring框架提供的注解,实现引用类型的赋值
spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName,byType。
@Autowired默认使用的是byType

位置:1)在属性定义的上面,无需set方法,推荐使用
2)在set方法上面

如果使用byName方式,需要做的是:
1.在属性上面加入@Autowired
2.在属性上面加入@Qualifer(value=“bean的id”):表示指定名称的bean完成赋值。

 */
@Autowired
@Qualifier(value ="school2")

private school school2;

/*
@Autowired
属性:required ,是一个boolean类型的,默认是true
required-true:表示引用类型赋值失败,程序报错,并终止执行
require-false:表示引用类型如果、赋值失败,程序正常执行,引用类型是null
推荐使用required-true,解决错误,有力正常解决问题。

 */
/*
@Resource注解
Spring提供了对 jdk中@Resource注解的支持。@Resource 注解既可以按名称匹配Bean,

也可以按类型匹配 Bean。支持byType,byName,默认是byName
默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。
位置:@Resource 可在属性上,也可在 set 方法上。
如果@Resource只使用byName方式,需要增加一个属性name
name的值是baen的id(名称)
*/

注解与 XML 的对比
注解优点是:
⚫ 方便
⚫ 直观
⚫ 高效(代码少,没有配置文件的书写那么复杂)。
其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。
XML 方式优点是: ⚫ 配置和代码是分离的
⚫ 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。

注解:不经常改变,xml经常改变。

AOP****
AOP(Aspect Orient Programming),面向切面编程。
切面:公共的,通用的,重复的功能称为切面,面向切面编程就是将切面提取出来,单独开发,在需要调用的方法中通过动态代理的方式进行织入.
手写AOP框架
业务:图书购买业务
切面:事务
1)第一个版本:业务和切面紧耦合在一起,没有拆分.
/**

  • 图书购买业务和事务切面耦合在一起
    */
    public void buy(){

    try {
        System.out.println("事务开启.......");
        System.out.println("图书购买业务功能实现...........");
        System.out.println("事务提交.......");
    } catch (Exception e) {
        System.out.println("事务回滚.......");
    }
    

    个人见解:这种方式就相当于明星自己唱歌自己布置场景,换个场景换首歌,都需要明星自己修改。

2)第二个版本:使用子类代理的方式拆分业务和切面.
//在父类中只有干干净净的业务
//BookServiceImpl
public void buy(){
System.out.println(“图书购买功能实现…”);
}
/**

  • 子类就是代理类,将父类的图书购买功能添加事务切面
    */
    //SubBookServiceImpl extends BookServiceImpl
    @Override
    public void buy() {
    try {
    //事务切面
    System.out.println(“事务开启…”);
    //主业务实现
    super.buy();
    //事务切面
    System.out.println(“事务提交…”);
    } catch (Exception e) {
    System.out.println(“事务回滚…”);
    }
    个人见解:这种方式就像明星教了个徒弟,明星只需要负责唱歌,布置场景需要徒弟即可,但是只有明星唱歌这一功能
    3)第三个版本:使用静态代理拆分业务和切面.业务和业务接口已拆分.此时切面紧耦合在业务中.
    //设计成员变量的类型为接口,为了灵活切换目标对象
    public Service target;

    //使用构造方法传入目标对象
    public Agent(Service target){
    this.target = target;
    }
    @Override
    public void buy() {
    try {
    //切面功能
    System.out.println(“事务开启…”); //日志 权限验证
    //业务功能
    target.buy();
    //切面功能
    System.out.println(“事务提交…”);
    } catch (Exception e) {
    System.out.println(“事务回滚…”);
    }
    public class BookServiceImpl implements Service {
    @Override
    public void buy() {
    System.out.println(“图书购买业务功能实现…”);
    }
    }
    public class ProductServiceImpl implements Service {
    @Override
    public void buy() {
    System.out.println(“商品购买业务实现…”);
    }
    public interface Service {
    //规定业务功能
    void buy();
    }
    个人见解:这种方式就像明星教了个徒弟,还请了个助理,布置场景需要助理即可,明星只需要负责唱歌,如果明星有事可以换徒弟,灵活切换目标。
    但是这些只是切换了目标,布置场景这些繁琐的同样的代码,换个地方又得重新布置。

4)第四个版本:使用静态代理拆分业务和业务接口,切面和切面接口.
//定义一个接口拆分功能,
public interface Service {
//规定业务功能
void buy();
}
public class ProductServiceImpl implements Service {
@Override
public void buy() {
System.out.println(“商品购买业务实现…”);
}
}
/**

  • 目标对象:业务功能的具体实现
    */
    public class BookServiceImpl implements Service {
    @Override
    public void buy() {
    System.out.println(“图书购买业务功能实现…”);
    }
    }
    //定义切面接口
    public interface AOP {
    default void before(){}
    default void after(){}
    default void exception(){}
    }
    切面的实现
    public class LogAop implements AOP {
    @Override
    public void before() {
    System.out.println(“前置日志输出…”);
    }
    }
    public class TransAop implements AOP {
    @Override
    public void before() {
    System.out.println(“事务开启…”);
    }

    @Override
    public void after() {
    System.out.println(“事务提交…”);
    }

    @Override
    public void exception() {
    System.out.println(“事务回滚…”);
    }
    }
    代理类,代理切面和功能的关系
    public class Agent implements Service {

    //传入目标(业务)对象,切面对象
    Service target;
    AOP aop;

    //使用构造方法初始化业务对象和切面对象
    public Agent(Service target,AOP aop){
    this.target = target;
    this.aop = aop;
    }

    @Override
    public void buy() {
    try {
    //切面
    aop.before(); //事务 日志
    //业务
    target.buy(); //图书 商品
    //切面
    aop.after(); //事务

    } catch (Exception e) {
        //切面
        aop.exception();
    }
    

    }
    }
    //这个代理传入两个对象,一个是功能的具体实现,一个是切面的实现,通过代理让两个的关系低耦合
    @Test
    public void test02(){
    Service agent = new Agent(new ProductServiceImpl(),new TransAop());
    //多个切面
    Service agent1 = new Agent(agent,new LogAop());
    agent1.buy();
    }

5)第五个版本:使用动态代理完成第四个版本的优化.
使用动态代理。
public class ProxyFactory {

public static Object getAgent(Service target,AOP aop){
    //返回生成的动态代理对象
    return Proxy.newProxyInstance(
            //类加载器
            target.getClass().getClassLoader(),
            //目标对象实现的所有的接口
            target.getClass().getInterfaces(),
            //代理功能实现
            new InvocationHandler() {
                @Override
                public Object invoke(
                        //生成的代理对象
                        Object proxy,
                        //正在被调用的目标方法buy(),show()
                        Method method,
                        //目标方法的参数
                        Object[] args) throws Throwable {
                    Object obj = null;
                    try {
                        //切面
                        aop.before();  //事务  日志
                        //业务
                        obj = method.invoke(target,args);
                        //切面
                        aop.after();

                    } catch (Exception e) {
                        //切面
                       aop.exception();
                    }

                    return obj; //目标方法的返回值
                }
            }
    );
}

}

DK和CGLib动态代理实现

动态代理在Java中有着广泛的应用,如Spring AOP,Hibernate数据查询、测试框架的后端mock、RPC,Java注解对象获取等。动态代理的代理关系是在运行时期确定的。接下来主要阐述两种动态代理的区别。

JDK和CGLib动态代理分析

自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方。JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler。其中,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑(如:我们在方法执行前后打印的日志,本文只是为了演示,实际的应用一般不会只是简单的打印日志的),并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。

JDK动态代理的话,他有一个限制,就是它只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,如何创建动态代理实例哪?答案就是CGLib。

CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。

JDK和CGLib动态代理区别

1、JDK动态代理具体实现原理:

通过实现InvocationHandler接口创建自己的调用处理器;

通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;

通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;

通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;

JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。

2、CGLib动态代理:

利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

3、两者对比:

JDK动态代理是面向接口的。

CGLib动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被final关键字所修饰,会失败。

4、使用注意:

如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);

如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。

JDK和CGLib动态代理性能对比

关于两者之间的性能的话,网上有人对于不通版本的jdk进行测试,
经过多次试验,测试结果大致是这样的,在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,
但是并没有教科书上的10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了,
但是JDK动态代理和CGLIB动态代理的适用场景还是不一样的哈!

动态代理:可以在程序的执行过程中国,创建代理对象。
通过代理对象执行方法,给目标了增加额外的功能(功能增强)
jdk动态代理实现步骤:
1.创建目标类,SomeServiceImpl目标类,给它的dosome和doother增加输出事件,事务。
2.创建OnvocationHandler接口实现类,在这个类实现目标方法增加功能。
3.使用jdk诶porxy创建代理对象,实现创建对象的能力。

aop
1.动态代理
实现方式:jdk动态代理,使用jdk中的proxy,Method,InvocationHanler创建代理对象。
oglib动态代理:第三方的工具库,创建代理对象,原理是继承,通过继承目标类,创建子类。
子类就是代理目标,要求目标不能是final的,方法也不能是final的
2。动态代理的作用:
1)加载目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码。
4)解耦合,让你的业务功能和日志,事务非事务功能分离。

3.AOP:面向切面编程。基于动态代理的,可以使用jdk,oglib两种代理方式。
Aop就是动态代理的规范化,把动态带路的实现步骤,方式都定义好了,让开发人员用已知统一的方式,就用动态代理。
aop(aspect Orient programing)
Aspect:切面:给你的目标类增加功能,就是切面,向上面用的日志,事务都是切面。
切面的特点,一般都是业务方法,独立使用的。
Orient:面向,对着。
programing:编程
oop:面向对象编程。
怎么理解面向切面编程?
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前,还是在目标方法后)
3.合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能。

Spring支持的AOP的实现
Spring支持AOP的编程,常用的有以下几种:
1)Before通知:在目标方法被调用前调用,涉及接口org.springframework.aop.MethodBeforeAdvice;
2)After通知:在目标方法被调用后调用,涉及接口为org.springframework.aop.AfterReturningAdvice;
3)Throws通知:目标方法抛出异常时调用,涉及接口org.springframework.aop.ThrowsAdvice;
4)Around通知:拦截对目标对象方法调用,涉及接口为org.aopalliance.intercept.MethodInterceptor。
public class LogAdvice implements MethodBeforeAdvice {
//相当于invokeCationHandler
/*
public class MyHandle implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
*/
@Override
public void before(Method method, Object[] args, Object o) throws Throwable {
SimpleDateFormat sf = new SimpleDateFormat(“yyyy-MM-dd”);
System.out.println(“\n[系统日志]”+sf.format(new Date())+“—”+method.getName()+ Arrays.toString(args));
}
}

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

<!--创建业务对象-->
<bean id ="bookServiceTarget" class="com.bjpowernode.service.impl.BookServiceImpl"></bean>
<!--创建切面的对象-->
<bean id="logAdvice" class="com.bjpowernode.advice.LogAdvice"></bean>

<!--绑定业务和切面-->
<bean id="bookService" class="org.springframework.aop.framework.ProxyFactoryBean">
    <!--spring自己封装好的代理对象
    @Nullable
private String[] interceptorNames;

@Nullable
private String targetName;
-->
    <!--配置业务接口-->
    <property name="interfaces" value="com.bjpowernode.service.BookService"></property>
    <!--配置切面-->
    <property name="interceptorNames">
        <list>
            <value>logAdvice</value>
        </list>
    </property>
    <!--织入-->
    <property name="target" ref="bookServiceTarget"></property>
</bean>

属于:
1)Aspect:切面表示增强的功能,就是一堆代码,完成某一个功能,非业务功能。
常见的切面功能有日志,事务,统计信息,参数检查,就是某个类中的业务方法。
2)Joinpoint:连接点,连接业务方法和切面的位置,就是类中的业务方法。
3)pointcut:切入点,指多个连接点方法的集合,多个方法。
4)目标对象:给哪个类的方法增加功能,这个类就是目标对象
5)Advice:通知,通知表示切面功能执行的时间。
AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。它因为是基于java语言开发的,所以无缝扩展.easy to learn and use(易学易用).

15.AspectJ常见通知类型
AspectJ 中常用的通知有四种类型:
1)前置通知@Before
2)后置通知@AfterReturning
3)环绕通知@Around
4)最终通知@After
5)定义切入点@Pointcut(了解)

.AspectJ 的切入点表达式(掌握)
规范的公式:
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
简化后的公式:
execution( 方法返回值 方法声明(参数) )

用到的符号:

  • 代码任意个任意的字符(通配符)
    … 如果出现在方法的参数中,则代表任意参数
    如果出现在路径中,则代表本路径及其所有的子路径
    访问权限和异常类型可以省略
    示例:
    execution(public * (…)) :任意的公共方法
    execution(
    set*(…)):任何一个以“set”开始的方法
    execution(* com.xyz.service.impl..(…)):任意的返回值类型,在com.xyz.service.impl包下的任意类的任意方法的任意参数
    execution(* com.xyz.service….(…)):任意的返回值类型 ,在com.xyz.service及其子包下的任意类的任意方法的任意参数
    com.xyz.service.a.b..(…) com.xyz.service..(…)
    execution(* …service..(…)):service之前可以有任意的子包
    execution(
    .service..*(…)):service之前只有一个包
    AspectJ的前置通知@Before
    在目标方法执行前切入切面功能.在切面方法中不可以获得目标方法的返回值,只能得到目标方法的签名.

实现的步骤:
添加依赖

org.springframework
spring-aspects
5.2.5.RELEASE

1)创建业务接口
2)创建业务实现
3)创建切面类,实现切面方法
4)在applicationContext.xml文件中进行切面绑定

项目案例:
@Aspect //交给AspectJ的框架去识别切面类
@Component
public class MyAspect {
/**
* 所有切面的功能都是由切面方法来实现的
* 可以将各种切面都在此类中进行开发
*
* 前置通知的切面方法的规范
* 1)访问权限是public
* 2)方法的返回值是void
* 3)方法名称自定义
* 4)方法没有参数,如果有也只能是JoinPoint类型 ----获取的是目标方法的签名
* 5)必须使用@Before注解来声明切入的时机是前切功能和切入点
* 参数:value 指定切入点表达式
*
* 业务方法
* public String doSome(String name, int age)
/
@Before(value = "execution(public String com.bjpowernode.s01.SomeServiceImpl.
(String,int))")
public void myBefore(){
System.out.println(“切面方法中的前置通知功能实现…”);
}

@Before(value = "execution(public * com.bjpowernode.s01.SomeServiceImpl.*(..))")
public void myBefore(){
    System.out.println("切面方法中的前置通知功能实现............");
}

@Before(value = "execution( * com.bjpowernode.s01.*.*(..))")
public void myBefore(JoinPoint jp){
    System.out.println("切面方法中的前置通知功能实现............");
    System.out.println("目标方法的签名:"+jp.getSignature());
    System.out.println("目标方法的参数:"+ Arrays.toString(jp.getArgs()));
}
@Before(value = "execution( * com.bjpowernode.s01..*(..))")
public void myBefore(){
    System.out.println("切面方法中的前置通知功能实现............");
}

@Before(value = "execution( * *(..))")
public void myBefore(){
    System.out.println("切面方法中的前置通知功能实现............");
}

}

18.AspectJ框架切换JDK动态代理和CGLib动态代理
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy> ===>默认是JDK动态代理,取时必须使用接口类型
<aop:aspectj-autoproxy proxy-target-class=“true”></aop:aspectj-autoproxy> ==>设置为CGLib子类代理,可以使用接口和实现类接
记住:使用接口来接,永远不出错.

19.@AfterReturning后置通知
后置通知是在目标方法执行后切入切面功能,可以得到目标方法的返回值.如果目标方法的返回值是简单类型(8种基本类型+String)则不可改变.如果目标方法的返回值是引用类型则可以改变.
@Aspect
@Component
public class MyAspect {
/**
* 后置通知的方法的规范
* 1)访问权限是public
* 2)方法没有返回值void
* 3)方法名称自定义
* 4)方法有参数(也可以没有参数,如果目标方法没有返回值,则可以写无参的方法,但一般会写有参,这样可以处理无参可以处理有参),这个切面方法的参数就是目标方法的返回值
* 5)使用@AfterReturning注解表明是后置通知
* 参数:
* value:指定切入点表达式
* returning:指定目标方法的返回值的名称,则名称必须与切面方法的参数名称一致.
/
@AfterReturning(value = "execution(
com.bjpowernode.s02..(…))",returning = “obj”)
public void myAfterReturning(Object obj){
System.out.println(“后置通知功能实现…”);
if(obj != null){
if(obj instanceof String){
obj = obj.toString().toUpperCase();
System.out.println(“在切面方法中目标方法的返回值:”+obj);
}
if(obj instanceof Student){
Student stu = (Student) obj;
stu.setName(“李四”);
System.out.println(“在切面方法中目标方法的返回值:”+stu);
}
}
}
}

20.环绕通知@Around
它是通过拦截目标方法的方式 ,在目标方法前后增强功能的通知.它是功能最强大的通知,一般事务使用此通知.它可以轻易的改变目标方法的返回值.
@Aspect
@Component
public class MyAspect {
/**
* 环绕通知方法的规范
* 1)访问权限是public
* 2)切面方法有返回值,此返回值就是目标方法的返回值
* 3)方法名称自定义
* 4)方法有参数,此参数就是目标方法
* 5)回避异常Throwable
* 6)使用@Around注解声明是环绕通知
* 参数:
* value:指定切入点表达式
*/

@Around(value = "execution(* com.bjpowernode.s03.*.*(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
    //前切功能实现
    System.out.println("环绕通知中的前置功能实现............");
    //目标方法调用
    Object obj = pjp.proceed(pjp.getArgs());
    //后切功能实现
    System.out.println("环绕通知中的后置功能实现............");
    return obj.toString().toUpperCase();  //改变了目标方法的返回值
}

}
21.最终通知@After
无论目标方法是否正常执行,最终通知的代码都会被执行.

22.给切入点表达式起别名@Pointcut
如果多个切面切入到同一个切入点,可以使用别名简化开发.
使用@Pointcut注解,创建一个空方法,此方法的名称就是别名.
@Aspect
@Component
public class MyAspect {
/**
* 最终通知方法的规范
* 1)访问权限是public
* 2)方法没有返回值
* 3)方法名称自定义
* 4)方法没有参数,如果有也只能是JoinPoint
* 5)使用@After注解表明是最终通知
* 参数:
* value:指定切入点表达式
*/
@After(value = “mycut()”)
public void myAfter(){
System.out.println(“最终通知的功能…”);
}

    @Before(value = "mycut()")
    public void myBefore(){
        System.out.println("前置通知的功能........");
    }

    @AfterReturning(value = "mycut()",returning = "obj")
    public void myAfterReturning(Object obj){
        System.out.println("后置通知的功能........");
    }
    @Around(value = "mycut()")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知中的前置通知的功能........");
        Object obj = pjp.proceed(pjp.getArgs());
        System.out.println("环绕通知中的后置通知的功能........");
        return obj;
    }

    @Pointcut(value = "execution(* com.bjpowernode.s04.*.*(..))")
    public void mycut(){}
}

说一个切面有三个关键的要求:
1)切面的功能代码,切面干什么
2)切面的执行位置,使用pointcut表示切面执行的位置
3)切面的执行时间,使用Adivce表示时间,在目标方法之前,还是目标方法之后。

5.aop的实现
aop是一个规范,是动态的一个规范化,一个标准
aop的技术实现框架:
1.spring在内部实现了aop规范,能做aop的工资。
spring主要在事务处理时使用aop。
我们项目开发中很少使用spring的aop实现,因为spring的aop比较笨重。
2.aspacetJ:一个开源的专门做aop的框架。spring框架中集成了aspectJ框架,通过spring就能使用aspecrj的功能。
aspacetJ框架实现aop有两种方式:
1.使用xml的配置文件。
2.使用注释,我们在项目中要做aop功能,一般使用注释,aspacetJ有五个注释。

6.学习aspectJ的框架的使用。
1)切面的执行时间,这个执行时间在规范中华叫做通知Advice(通知,增强)。
在aspectJ框架中使用注解表示,
也可以使用xml配置文件中的标签。
1.@Before
2.@AfterReturning
3.@Around
4.@AfterThrowing
5.@After

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
解释:
modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选的部分
AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。
以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可
以使用以下符号:

  • 表示0至多个字符
    …用在方法参数中,表示任意多个参数,
    用在包名后表示当前包及其子包路劲
    +用在类名后,表示当前类及其子类
    用在接口后,表示当前接口及其实现类。

举例:
execution(public * (…))
指定切入点为:任意公共方法。
execution(
set*(…))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service..(…))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service….(…))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“…”出现在类名中时,后
面必须跟“”,表示包、子包下的所有类。
execution(
…service..(…))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(
.service..(…))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(
.ISomeService.(…))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution(* …ISomeService.(…))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution(* com.xyz.service.IAccountService.(…))
指定切入点为:IAccountService 接口中的任意方法。
execution(
com.xyz.service.IAccountService+.(…))
指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意
方法;若为类,则为该类及其子类中的任意方法。
execution(
joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参
数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用
全限定类名,如 joke( java.util.List, int)。
execution(* joke(String,)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类
型,如joke(String s1,String s2)和joke(String s1,double d2)都是,但joke(String s1,double d2,String
s3)不是。
execution(
joke(String,…)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有任意个参数且
参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3)
都是。
execution(* joke(Object))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob)
是,但,joke(String s)与 joke(User u)均不是。
execution(* joke(Object+)))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。
不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。

使用aspectJ的实现步骤
1.新建maven项目。
2.加入依赖
1)spring依赖
2)sapectJ依赖
3)junit单元测试
3.创建目标类,接口和它的实现类,
要做的是给类中的方法增加功能。
4.创建切面类:普通类。
1)在类的上面加入@Aspect
2)在类中定义方法,方法就是切面要执行的功能代码
在方法的上面加入aspectJ中的通知注解,例如@Before
有需要指定切入点表达式execution()
5.创建spring的配置文件:声明对象,把对象交给容器统一管理。
声明对象你可以使用注解或者xml配置文件
1)声明目标对象
2)声明切面类对象
3)声明aspectJ框架中的自动化代理生成器标签。
自动化代理生成器:用来完成代理对象的自动化创建功能。

6.创建测试类,从spring容器中获取目标对象(实际上就是代理对象)。通过代理执行方法,实现aop的功能增强。

package com.anyu.bao1;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Date;

/*
@Aspect:是aspectJ框架中的注解
作用:表示当前类是切面类。
切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码。
位置:在类定义的上面
/
@Aspect
public class MyaspectJ {
/
*
*定义方法,方法是实现切面功能的
*方法的定义要求

  • 1.公共方法public

  • 2.方法没有返回值

  • 3.方法名称自定义

  • 4.方法可以由参赛,也可以没有参数

  • 如果有参数,参数是自定义的,有几个参数类型可以使用
    */

    /**

    • @Before :前置通知注解
    • 属性:value ,是切入点表达式,表示切面的功能执行位置。
    • 位置:在方法的上面
      *特点:
    • 1.在目标方法之前先执行
    • 2.不会改变目标方法的执行结果
    • 3.不会影响目标方法的执行

    */
    @Before(value = “execution(public void com.anyu.bao1.someServiceImpl.dosome(String,Integer))”)
    public void myBefore(){
    System.out.println(“前置功能,切面功能:在目标方法之前输出事件”+new Date());
    }
    }

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

<!-- 声明目标对象-->
<bean id="someService" class="com.anyu.bao1.someServiceImpl"/>

<!-- 声明切面类对象-->
<bean id="beforeaspect" class="com.anyu.bao1.MyaspectJ"/>
<!-- 声明自动化代理生成器,使用aspectj框架内部的功能,创建目标对象的代理对象。
创建代理对象是在内存中实现的,修改目标对象的内存中的结构,
创建为代理对象,所有目标对象就是被修改后的代理对象
aspect:autoproxy:会把sapring容器中的所有目标多谢,一次性都生成代理对象
-->
<aop:aspectj-autoproxy/>
方法签名: 其实对于同名不同类、同类不同名的方法,方法签名的意义并不是很大,但 是对于重载方法来说,方法签名的意义就十分巨大了。由于重载方法 之间的方法名是相同的,那么我们势必要从构成方法的其他几个要素中 找到另一个要素与方法名组成能够唯一标示方法的签名,方法体当然不予考虑 。那么就是形参列表和返回值了,但是由于对于调用方法的人来说,方法的形参数 据类型列表的重要程度要远远高于返回值,所以方法签名就由方法名+形参列表构成, 也就是说,方法名和形参数据类型列表可以唯一的确定一个方法,与方法的返回值一点 关系都没有,这是判断重载重要依据,所以,以下的代码是不允许的。 /* 指定通知方法中的参数JoinPoint JoinPoint:业务方法,要加入切面功能的业务方法 作用是:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参。 如果你的切面功能中要用到方法的信息,就加入JoinPoint 这个JoinPoint参数的信息是又框架赋予的,必须是第一位置的参数 */ @Before(value = "execution(void do*(..))") public void myBefore2(JoinPoint jp){ //获取方法的的完整定义 System.out.println("方法的签名(定义)"+jp.getSignature()); //获取方法的名称 System.out.println("方法的名称"+jp.getSignature().getName()); //获取方法的实参 Object sc[]=jp.getArgs(); for(Object args:sc){ System.out.println("方法的实参"+args); } // System.out.println(""+jp.getSignature().getModifiers()); System.out.println("2前置功能,切面功能:在目标方法之前输出事件"+new Date()); }

前置通知:
/*
@Aspect:是aspectJ框架中的注解
作用:表示当前类是切面类。
切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码。
位置:在类定义的上面
/
@Aspect
public class MyaspectJ {
/
*
*定义方法,方法是实现切面功能的
*方法的定义要求

  • 1.公共方法public

  • 2.方法没有返回值

  • 3.方法名称自定义

  • 4.方法可以由参赛,也可以没有参数

  • 如果有参数,参数是自定义的,有几个参数类型可以使用
    */

    /**

    • @Before :前置通知注解
    • 属性:value ,是切入点表达式,表示切面的功能执行位置。
    • 位置:在方法的上面
      *特点:
    • 1.在目标方法之前先执行
    • 2.不会改变目标方法的执行结果
    • 3.不会影响目标方法的执行

    /
    /

    其实还是动态代理
    时间:@Before
    位置:(value = “execution(public void com.anyu.bao1.someServiceImpl.dosome(String,Integer))”)
    功能:public void myBefore(){
    System.out.println(“前置功能,切面功能:在目标方法之前输出事件”+new Date());
    }
    /
    @Before(value = “execution(public void com.anyu.bao1.someServiceImpl.dosome(String,Integer))”)
    public void myBefore(){
    System.out.println(“1前置功能,切面功能:在目标方法之前输出事件”+new Date());
    }
    // @Before(value = “execution(void dosome(String,Integer))”)
    // public void myBefore2(){
    // System.out.println(“2前置功能,切面功能:在目标方法之前输出事件”+new Date());
    // }
    // @Before(value = "execution(void do
    (…))")
    // public void myBefore2(){
    // System.out.println(“2前置功能,切面功能:在目标方法之前输出事件”+new Date());
    // }

    /*
    指定通知方法中的参数JoinPoint
    JoinPoint:业务方法,要加入切面功能的业务方法
    作用是:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参。
    如果你的切面功能中要用到方法的信息,就加入JoinPoint
    这个JoinPoint参数的信息是又框架赋予的,必须是第一位置的参数
    */
    @Before(value = “execution(void dosome(…))”)
    public void myBefore2(JoinPoint jp){
    //获取方法的的完整定义
    System.out.println(“方法的签名(定义)”+jp.getSignature());
    //获取方法的名称
    System.out.println(“方法的名称”+jp.getSignature().getName());
    //获取方法的实参
    Object sc[]=jp.getArgs();
    for(Object args:sc){
    System.out.println(“方法的实参”+args);
    }
    // //获取形式参数
    // String argNames =jp.getSignature().getDeclaringTypeName();// 参数名
    System.out.println(“”+jp.getSignature().getModifiers());
    System.out.println(“2前置功能,切面功能:在目标方法之前输出事件”+new Date());
    }
    }

后置通知:
/*
切面类一定要有@Aspect注解,否则就查不到这个切面类
*/
@Aspect
public class backMyspectj {
/**后置通知
*定义方法,方法是实现切面功能的
*方法的定义要求
* 1.公共方法public
* 2.方法没有返回值
* 3.方法名称自定义
* 4.方法有参数的,推荐是Object:参数自定义名。
*
*/

/**
 * @afterReturning后置通知注解
 * 属性:value ,是切入点表达式,表示切面的功能执行位置。
 * 2.returning自定义的变量,表示目标方法的返回值的。
 * 自定义变量名必须跟通知方法的形参名一样
 * 位置:在方法的上面
 *特点:
 * 1.在目标方法之后执行
 * 2.能够获取目标方法的返回值,可以根据这个返回值做不同的处理功能
 * 3.可以修改这个返回值
 *
 * 后置通知的执行:
 * Object res=doother();
 * myAfterReturning(res);
 * System.out.print("res="+res);
 *
 */
@AfterReturning(value="execution(* *..someServiceImpl.doother(..))",returning = "res")
public void myAfterReturning(Object res){

//Object res:是目标方法执行后的返回值,根据返回值做你的切面功能处理
System.out.println(“后置通知:在目标方法执行后的返回值:”+res);
}
}

环绕通知:
@Aspect
public class aroundMyaspectkj {
/**环绕通知方法的定义格式
*定义方法,方法是实现切面功能的
*方法的定义要求
* 1.公共方法public
* 2.方法有返回值,推荐使用Object
* 3.方法名称自定义
* 4.方法有参数的,固定的参数ProceedingJoinpoint
*
*/

/*
@Around:环绕通知
属性:value 切入点表达式
位置:在方法的定义上面
特点:
1.它是功能最强的通知
2.在目标方法前后都能增强功能
3.控制目标方法是否被调用
4.修改原来目标方法的执行结果,影响最后的调用结果
环绕通知:等同于jdk动态代理的,InvocationHandler接口。

参数:ProceedingJoinPoint就等同于Method
作用:执行目标方法的
返回值:就是目标方法的执行结果,可以被修改。
环绕通知:经常组事务,在目标方法执行之前开启事务,执行方法只会,关闭事务
/
@Around(value = "execution(
*…someServiceImpl.dothree(…))“)
public Object myAroud(ProceedingJoinPoint pj) throws Throwable {
Object result=null;
//ProceedingJoinPoint继承JoinPoint也能获取参数信息
Object args[]=pj.getArgs();
String name=”";
if(args!=null&&args.length>1){
Object arg=args[0];
name= (String)arg;
}

   System.out.println("环绕通知,在方法执行前调用"+new Date());
 // result= pj.proceed();///pj相当于jdk动态代理的Method,这个代码相当与Method.invoke()
   if("王五".equals(name)){
       result= pj.proceed();///pj相当于jdk动态代理的Method,这个代码相当与Method.invoke()
   }
   System.out.println("环绕通知:在方法后之后执行,提交事务");

//修改返回值,最终的值将会改变
if (result!=null){
result=“这个结果改变啦”;
}
return result;//这个返回值是最终的返回值
}
}

环绕通知:
@Aspect
public class AfterThrowingMyaspectj {
/**异常通知的方法定义格式
*定义方法,方法是实现切面功能的
*方法的定义要求
* 1.公共方法public
* 2.方法没有返回值
* 3.方法名称自定义
* 4.方法有一个Exception,如果还有就是JoinPoint
*
*/

/**
 * @afterThrowing后置通知注解
 * 属性:value ,是切入点表达式,表示切面的功能执行位置。
 * 2.throwing自定义的变量,表示目标方法抛出的异常对象
 * 自定义变量名必须跟通知方法的形参名一样
 * 位置:在方法的上面

特点:1.在目标方法抛出异常时候执行
2.可以做异常的监控程序,监控目标方法执行时是不是有异常。
如果有异常,可以发送邮件,短信通知。
执行就是:try{
someServiceImpl.doSecond(…)}
catch(Exeception ex){
public void Afterthrowing(e){

}
}
 */
@AfterThrowing(value = "execution(* *..someServiceImpl.dofore())",throwing = "ex")
public void Afterthrowing(Exception ex){

// 如果有异常,可以发送邮件,短信通知。

    System.out.println("异常通知:在目标方法执行后的返回值:"+ex.getMessage());
}

}
/*
最终通知方法的定义格式:
1.public
2.没有返回值
3.方法名称自定义
4.方法没有参数,如果有那就是JoinPoing
/
/

@After:最终通知
属性:value 切入点表达式
位置:在方法的上面
特点:1.总是会执行
2.在目标方法之后执行的
相当于
try{someServiceImpl.dofive(…)
}
catch(Exception ex){
}finally{myAfter()
}
/
@After(value = "execution(
*…someServiceImpl.dofive(…))")
public void myAfter(){
System.out.println(“执行最终通知,在方法执行之后一定会自行”);
//一般用来做资源清除
}
@Aspect
public class pointcutAspect {
@Before(value = “mybt()”)
public void myBefore(){
System.out.println(“1前置功能,切面功能:在目标方法之前输出事件”+new Date());
}
//注意括号!!
@After(value = “mybt()”)
public void myafter(){
System.out.println(“最终通知:最后执行的事件”+new Date());
}

/*
@pointcut:定义和管理切入点,如果你的项目有多个切入点表达式是重复的,可以复用的,
可以使用pointcut
属性:value 切入点表达式
位置:在自定义的方法上面
特点:
当使用pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名。
其他的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
 */
@Pointcut(value = "execution(public void com.anyu.bao1.someServiceImpl.dopointcut(..))")

private void mybt(){

}

}

cglib
JDK动态代理是只能代理接口的,因为代理类是继承proxy类的,并实现了proxy的多个接口
一个类可以实现多个接口,但是只能继承一个类,所以只能实现接口,
在spring切面中,如果没有接口,那么jdk就不会代理,而变成cglib动态代理,
目标类没有接口,使用cglib动态代理,spring框架会自动应用cglib
如果期望目标类有接口:使用cglib代理
使用xml配置
<aop:apectj-autoproxy proxy-taget-class=“true”>告诉框架使用cglib动态代理

第四章:把mybatis框架和spring集成在一起,向一个框架一样使用。
开发技术是:IOC
为什么IOC能把mybatis和spring集成在一起,像一个框架,是因为IOC能创建对象。
可以把mybatis框架中的对象交给spring统一创建,开发人员从spring中获取对象。
开发人员就不用同时面对两个或多个框架了,就面对一个spring

mybatis使用步骤,对象:
1.定义dao接口,studentdao
2.定义mapper文件,studentDao.xml
3.定义mybatis的主配置文件mybatis.xml
4.创建dao的代理对象:StudentDao dao=SqlSession.getMapper(StudentDao.class);
List students=dao.selectStudents();

要使用dao对象,需要使用getMapper()方法。
怎么能使用getMapper()方法。需要哪些条件。
1.获取Sqlsession对象,需要使用SqlSessionFactory的openSession()方法。
2.创建SqlSessionFactory对象,通过读取mybatis的主配置文件,能创建SqlSessionFactory对象。
需要SqlSessionFactory对象,使用Factory能读取SqlSession,有了SqlSession就能有dao,
Factory创建需要读取配置文件。

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

通过以上的说明,我们需要spring创建以下对象
1.独立的连接池类,使用阿里的druid连接池
2.SqlSessionFactory对象
3.创建出dao对象。

三个对象的创建语法,使用xml的bean标签。

spring和mybatis的集成
步骤:
1.新建maven项目
2.加入maven依赖
1)spring的依赖
2)mybatis依赖
3)mysql的驱动
4)spring的事务依赖
5)mybatis和spring集成的依赖,mybatis官方用的,
用来在spring项目中创建mybatis的SqlSessionFactory,dao对象的
3.创建实体类
4.创建dao接口和mapper文件
5.创建mybatis主配置文件。
6.创建service接口和实现类,属性是dao
7.创建spring的配置文件,声明mybatis的对象交给spring创建
1)数据源DataSource
2)SqlSessionFactory
3)Dao对象
4)声明自定义的service
8.创建测试类,获取service对象,通过service调用dao完成数据库的访问

常用约束:

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


简单类型赋值用value,引用类型赋值用ref
SqlSessionFactory是简单类型,赋值使用的是value

事务***
1.什么是事务
讲mysql的时候,提出了事务,事务是指一组sql语句的集合,集合中有多条sql语句
,可能是insert,update,select,delete,我们希望这些sql语句都能成功,或者都失败
,这些sql语句执行是一致的,作为一个整体执行。

2.在什么时候想到使用事务
当我的操作,涉及多个表,或者多个sql语句的insert,update,delete,
需要包中这些语句都是成功才能完成我的功能,或者都失败,保证在操作是符合要求的
在java代码中写程序,控制事务,此时,事务应该放在service类的业务方法上,执行多个sql语句。

3.通常使用JDbc访问数据库,还是mybatis访问数据库怎么处理事务
JDBC访问数据库,处理事务,Connection conn;
conn.commot();conn.rollback();
mybatis访问数据可,处理事务。SqlSession.commit();
SqlSession.rollback();
hibernate访问数据数据库,处理事务,Session.commit();Session.rollback();
4.3问题中事务的处理方式有什么不足
1)不同的数据库访问技术,处理事务的对象,方法不同。需要了解不同数据库访问技术使用事务的原理。
2)掌握多种数据库中事务的处理逻辑,什么时候提交事务,什么时候回顾事务
3)处理事务的多种方法。

总结:就是多种数据库的访问技术,若不同的事务处理的机制,对象,方法。

5.怎么解决不足
spring提供一种处理事务的统一模型,能使用统一步骤,方式完成多种不同数据库访问 技术的事务处理。
使用spring的事务处理机制,可以完成mybatis访问数据库的事务处理。
使用spring的事务处理机制,可以完成hibernate访问数据库的事务处理。

6.处理事务,需要怎么做,做什么。
spring处理事务的模型,使用的步骤都是固定的,把事务使用的信息提供给spring就可以了
1)事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback,
接口:platfontTransactionManager,定义了事务重要方法,commit,rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库—spring创建好的是DataSourceTransactionManager
hibernate访问数据库—spring创建的是HibernateTransactionManager

怎么使用:你需要告诉spring,你用是那种数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对于事务管理器实现类,在spring的配置文件中使用
声明就可以了,

2)你的业务方法需要什么样的事务,说明事务的类型。
说明方法需要的事务:
1)事务的隔离级别
DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle
默认为 READ_COMMITTED。 ➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
➢ SERIALIZABLE:串行化。不存在并发问题。
2)事务的超时时间:表示一个方法最长的执行时间,如果方法执行时间超过了时间,事务就回滚。
单位是秒,整数值,默认值是-1.
3)事务的传播行为,控制事务的方法是不是有事务的,是什么样的事务的。
7个传播行为,表示你的业务方法调用时,事务在方法之间是如何使用的。
所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情
况。如,A 事务中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的
维护情况,就称为事务传播行为。事务传播行为是加在方法上的。
事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。
PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS
以上三个需要掌握
PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED

3)事务提交事务,回滚事务的时机。
1)当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务,
事务管理器commit
2)当你的业务方法抛出运行时异常,spring执行回滚,调用事务管理器的rollback
运行时异常的定义,RuntimeException,和他的子类都是运行时异常,
例如NullPointException,NumberFormatException
3)当你的业务方法抛出非运行时异常,主要是受查异常时,提交事务受查异常,(受查异常就是编译时的异常)
在你写代码中,必须处理的异常,例如IOException,SQLException

总结spring的事务
1.管理事务的是 事务关系和他的实现类。
2.spring的事务是一个统一模型
1)指定要使用的事务管理实现类,使用
2)指定哪些类,哪些方法需要加入事务的功能
3)指定方法需要的隔离级别,传播行为,超时

你需要告诉spring,你的项目中类信息,方法的名称,方法的事务传播行为。
mybtis是自动提交的

spring框架中提供的事务处理方案
1.适合中小项目自己用aop实现黑业务增加事务的功能,使用@Tracnsactional注解增加事务
@Transactional注解是spring框架自己注释,放在public方法的上面,表示当前方法具有事务,
可以给注解的属性数值,表示具体的隔离级别,传播行为,异常信息等等。

使用@Transactional的步骤
去。需要声明事务管理器对象

2.开启事务注解驱动,告诉spring框架,我要使用注解的方式管理事务。
spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
spring给业务方法加入事务:
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知
@Around(“你要增加的业务功能的业务方法名称”)
Object myAround(){
开始事务,spring给你开启
try{
buy(1001,10);
spring的事务管理.commit();}
catch(Execption e){
spring的事务管理.rollback();

}
}
3.在你的方法上面加入@Transational

步骤:
实现注解的事务步骤:
复制 trans_sale 项目,新项目 trans_sale_annotation


org.springframework
spring-tx
5.2.5.RELEASE

  1. 声明事务管理器
  2. 开启注解驱动
    transaction-manager:事务管理器 bean 的 id
  3. 业务层 public 方法加入事务属性

!!!!!!!!!!!!!!!!!!!!!!踩坑踩坑!!!!!
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.anyu.dao.GoodsDao.selectgoods
这个错误找了很久都没有错,最后发现是mapper.xml文件的名字写错了,必须跟dao的名字一致,因为




通过这个映射,类名就是包名

2.适用大型项目,有许多的类,方法,需要大量的配置事务,适用aspectJ框架功能,在spring配置文件中声明类,
方法需要的事务这种方式业务方法和事务配置,这种方式业务方法和事务配置完全分离。

实现步骤:都是在xml配置文件中实现:
1)
新加入 aspectj 的依赖坐标

org.springframework
spring-aspects
5.2.5.RELEASE

2)声明事务管理器对象

3)声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)
4)配置aop:指定哪些类需要创建代理

<!-- aspectJ事务,适合大型项目-->
<!-- 1.配置事务管理器-->

在 Web 项目中使用 Spring 框架,首先要解决在 web 层(这里指 Servlet)中获取到 Spring
容器的问题。只要在 web 层获取到了 Spring 容器,便可从容器中获取到 Service 对象。
6.1Web 项目使用 Spring 的问题(了解)
举例:springWeb 项目(在 spring-mybatis 基础上修改)
Step1:新建一个 Maven Project
类型 maven-archetype-webapp
Step2: 复制代码,配置文件,jar
将 spring-mybatis 项目中以下内容复制到当前项目中:
(1)Service 层、Dao 层全部代码
(2)配置文件 applicationContext.xml 及 jdbc.properties,mybatis.xml
(3)pom.xml
(4)加入 servlet ,jsp 依赖
在之前原有的 pom.xml 文件中再加入以下的内容:

javax.servlet javax.servlet-api 3.1.0 provided javax.servlet.jsp jsp-api 2.2.1-b03 provided Step3:定义 index 页面 Step4:定义 RegisterServlet(重点代码) Step5:定义 success 页面 北京动力节点 www.bjpowernode.com 讲师王鹤 60 Step6:web.xml 注册 Servlet Step7:运行结果分析 当表单提交,跳转到 success.jsp 后,多刷新几次页面,查看后台输出,发现每刷新一次 页面,就 new 出一个新的 Spring 容器。即,每提交一次请求,就会创建一个新的 Spring 容 器。对于一个应用来说,只需要一个 Spring 容器即可。所以,将 Spring 容器的创建语句放 在 Servlet 的 doGet()或 doPost()方法中是有问题的。 此时,可以考虑,将 Spring 容器的创建放在 Servlet 进行初始化时进行,即执行 init()方 法时执行。并且,Servlet 还是单例多线程的,即一个业务只有一个 Servlet 实例,所有执行 该业务的用户执行的都是这一个 Servlet 实例。这样,Spring 容器就具有了唯一性了。 但是,Servlet 是一个业务一个 Servlet 实例,即 LoginServlet 只有一个,但还会有 StudentServlet、TeacherServlet 等。每个业务都会有一个 Servlet,都会执行自己的 init()方法, 也就都会创建一个 Spring 容器了。这样一来,Spring 容器就又不唯一了。 6.2使用 Spring 的监听器 ContextLoaderListener(掌握) 举例:springweb-2 项目(在 spring-web 项目基础上修改) 对于 Web 应用来说,ServletContext 对象是唯一的,一个 Web 应用,只有一个 ServletContext 对象,该对象是在 Web 应用装载时初始化的。若将 Spring 容器的创建时机, 放在 ServletContext 初始化时,就可以保证 Spring 容器的创建只会执行一次,也就保证了 Spring 容器在整个应用中的唯一性。 当 Spring 容器创建好后,在整个应用的生命周期过程中,Spring 容器应该是随时可以被

访问的。即,Spring 容器应具有全局性。而放入 ServletContext 对象的属性,就具有应用的
全局性。所以,将创建好的 Spring 容器,以属性的形式放入到 ServletContext 的空间中,就
保证了 Spring 容器的全局性。
上述的这些工作,已经被封装在了如下的 Spring 的 Jar 包的相关 API 中:
spring-web-5.2.5.RELEASE
Step1:maven 依赖 pom.xml
org.springframework spring-web 5.2.5.RELEASE

Step2:注册监听器 ContextLoaderListener
若要在 ServletContext 初 始 化 时 创 建 Spring 容 器 , 就 需 要 使 用 监 听 器 接 口
ServletContextListener 对 ServletContext 进行监听。在 web.xml 中注册该监听器。
Spring 为该监听器接口定义了一个实现类 ContextLoaderListener,完成了两个很重要的
工作:创建容器对象,并将容器对象放入到了 ServletContext 的空间中。
打开 ContextLoaderListener 的源码。看到一共四个方法,两个是构造方法,一个初始化
方法,一个销毁方法。
所以,在这四个方法中较重要的方法应该就是 contextInitialized(),context 初始化方法。
跟踪 initWebApplicationContext()方法,可以看到,在其中创建了容器对象。
北京动力节点 www.bjpowernode.com 讲师王鹤
63
并且,将创建好的容器对象放入到了 ServletContext 的空间中,key 为一个常量:
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。
Step3:指定 Spring 配置文件的位置
ContextLoaderListener 在对 Spring 容器进行创建时,需要加载 Spring 配置文件。其默认
的 Spring 配置文件位置与名称为:WEB-INF/applicationContext.xml。但,一般会将该配置文
件放置于项目的 classpath 下,即 src 下,所以需要在 web.xml 中对 Spring 配置文件的位置及
名称进行指定。
从监听器 ContextLoaderListener 的父类 ContextLoader 的源码中可以看到其要读取的配
置文件位置参数名称 contextConfigLocation。
Step4:获取 Spring 容器对象
在 Servlet 中获取容器对象的常用方式有两种:
(1) 直接从 ServletContext 中获取 从对监听器 ContextLoaderListener 的源码分析可知,容器对象在 ServletContext 的中存
放的 key 为 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。所以,可
以直接通过 ServletContext 的 getAttribute()方法,按照指定的 key 将容器对象获取到。
(2) 通过 WebApplicationContextUtils 获取
工具类 WebApplicationContextUtils 有一个方法专门用于从 ServletContext 中获取 Spring
容器对象:getRequiredWebApplicationContext(ServletContext sc)
调用 Spring 提供的方法获取容器对象:
查其源码,看其调用关系,就可看到其是从 ServletContext 中读取的属性值,即 Spring
容器。
以上两种方式,无论使用哪种获取容器对象,刷新 success 页面后,可看到代码中使用
的 Spring 容器均为同一个对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值