Spring
Maven
-
核心概念
-
pom:项目对象模型,构建maven项目,管理jar包依赖。
<modelVersion>4.0.0</modelVersion> <!--maven模型的版本 -->
<groupId>org.example</groupId> <!--组织id,一般为公司域名的倒写或域名倒写+项目名字 -->
<artifactId>Spring1</artifactId> <!--项目名称 -->
<version>1.0-SNAPSHOT</version> <!-- 项目版本号,通常在版本后加-SNAPSHOT(快照)-->
<!-- 搜索使用的中央仓库:https://mvnrepository.com/,使用groupId或者artifactId搜索 -->
<packaging> <packaging> 项目打包的类型,默认为jar
<dependencies>
<dependency> <!--添加依赖,从本地仓库中查找,没有从远程下载。-->
<!-- 坐标 -->
<scope> </scope> <!--依赖使用的范围:compile(默认,所有阶段),test,provided(编译,测试时有效,打包部署时不需要)-->
</dependency>
</dependencies>
<properties> <!-- 设置属性 -->
<!--自定义全局变量,一般为统一版本号-->
<name>版本号</name>
<!--使用方式:在其他地方${name} -->
</properties>
<build> <!--maven项目在构建是,配置信息,使用的插件或者编译使用的jdk-->
<plugins>
<plugin>
<configuration>
<source> </source> <!--编译jdk版本-->
<target> </target> <!--运行jdk版本-->
</configuration>
</plugin>
<resources>
<resource>
<!--
没有该标签时,mavn将src/main/resources拷贝到target/classes下,src/main/java目录下的其他文件不拷贝
-->
<!-- 该标签:需要将src/main/java目录下的其他文件不拷贝-->
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<!-- *.properties已经启过滤器作用-->
<filtering>false</filtering>
</resource>
</resources>
</plugins>
</build>-
约定的目录结构:
HELLO <!--项目文件夹 -->
|---src
|---|---main <!--主程序 -->
|---|---|---java <!--主程序java文件和程序包-->
|---|---|---resources <!--主程序使用的配置文件-->
|---|---test
|---|---|---java <!--测试java文件和程序包-->
|---|---|---resources <!--测试使用的配置文件-->
|---pom.xml <!--maven的配置文件--> -
坐标:唯一的字符串,用于表示资源,必须
<groupId>org.example</groupId> <!--组织id,一般为公司域名的倒写或域名倒写+项目名字-->
<artifactId>Spring1</artifactId> <!--项目名称-->
<version>1.0-SNAPSHOT</version> <!--项目版本号,通常在版本后加-SNAPSHOT(快照)--> -
依赖管理:管理jar包依赖。
-
仓库管理:资源存放的仓库管理
- 存放maven使用的jar包和项目使用的jar包
- 分类
- 本地仓库:在maven 目录下settings.xml中配置本地仓库
- 远程仓库:需要联网下载
- 中央仓库(欧洲)
- 中央仓库的镜像。
- 私服:局域网中使用的仓库。
- 仓库使用过程
- 首先查看本地仓库
- 在私服中查找
- 镜像中查找
- 中央仓库
-
生命周期:maven构建项目过程,清理,编译,测试,报告,打包,安装,部署
-
-
常用命令:使用jar文件中的插件实现
mvn clean #--清理target文件
mvn compile #编译src/main目录下所有java文件,生成target文件
mvn test-compile
mvn test #会执行前面的步骤
mvn package #打包:会进行编译,测试编译,测试,按照pom文件进行打包生成在target目录下,只有src/main中的东西,
mvn install #将本地工程按照坐标保存到本地仓库
mvn deploy #将本地工程按照坐标保存到本地仓库,保存到私服仓库中,且自动将项目部署到web容器中
IOC
-
控制反转:对象创建,赋值,管理工作由代码之外的容器外部资源实现。
- 控制:创建对象,属性赋值,对象之间的关系管理
- 反转:创建对象的权限转移给代码之外的容器实现。由容器代替开发人员管理对象。创建对象,给属性赋值。
- 容器:服务器\框架。
- IOC目的:减少代码改动,实现更多的功能,实现解耦合。
-
创建对象:
-
构造方法:
-
反射:
-
Java特征之一,允许运行中的java程序获取自身的信息,并且操作类或者对象的内部属性。
-
通过反射,可以在运行时获得程序中的每一个类型的成员活动成员的信息。
-
对象是在编译时确定的,java反射机制可以动态地创建对象并且调用相关的属性,这些对象的类型在编译时未知。
-
因此可以通过反射创建对象。即使在编译时对象的类型是未知的。
-
反射提供功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法,可以通过反射调用private方法
- 在运行时调用任意一个对象的方法
-
反射的核心思想:JVM在动态运行时才加载类,调用方法或者访问属性,不需要再编译的时候知道运行的对象是什么。
-
原理:
-
class的加载:JVM使用ClassLoader将编译好的class文件加载到方法区内存中:此方法返回一个Class对象
Class clazz = ClassLoader.getSystemClassLoader().loadClass("com.mypackage.MyClass");
详细一点的过程:
-
类加载器:加载.class文件步骤
-
ReflectionData缓存:
-
提高反射性能,Class内中有一个useCaches静态变量标记是否使用缓存。外部使用sun.reflect.noCaches来禁用缓存。Class内中有一个ReflectionData内部类用于存放反射数据的缓存,声明了一个reflectionData域,按照需要延迟加载并没有指向一个实例化的ReflectionData对象。
-
Class类:Class类的构造器是私有的,因此无法手动new一个Class对象,只能由JVM创建,JVM通过类加载器创建Class对象。
-
Class对象:修饰符,类名,参数化信息(泛化信息),接口,注解,字段,构造器,方法。
// 标记是否使用缓存,可以通过外部的sun.reflect.noCaches配置是否禁用缓存
private static boolean useCaches = true;
static class ReflectionData<T> {
volatile Field[] declaredFields;
volatile Field[] publicFields;
volatile Method[] declaredMethods;
volatile Method[] publicMethods;
volatile Constructor<T>[] declaredConstructors;
volatile Constructors<T>[] publicConstructors;
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
final int redefinedCount;
ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}
// 这个是SoftReference,在内存资源紧张的时候可能会被回收
// volatile保证多线程环境下读写的正确性
private volatile transient SoftReference<RefelectionData<T>> reflectionData;
// 这个主要用于和ReflectionData中的redefinedCount进行比较
// 如果两个值不相等,说明ReflectionData缓存的数据已经过期了
private volatile transient classRedefinedCount = 0; -
Class构造器为私有的,只能由JVM创建,需要传入类加载器。
-
-
反射的运用:
-
用于框架的开发
-
各种容器实现的核心
-
反射中各类API:依赖与四个类:Class,Constructor,Field,Method;
-
获得构造器的方法:
Constructor getConstructor(Class[] params) //获得使用特殊的参数类型的公共构造函数,
Constructor[] getConstructors() //获得类的所有公共构造函数 Constructor
Constructor getDeclaredConstructor(Class[] params) //获得使用特定参数类型的构造函数(与接入级别无关)
Constructor[] getDeclaredConstructors() //获得类的所有构造函数(与接入级别无关) -
获取字段信息(属性):
Field getField(String name) //获得命名的公共字段
Field[] getFields() //获得类的所有公共字段
Field getDeclaredField(String name) //获得类声明的命名的字段
Field[] getDeclaredFields() //获得类声明的所有字段 -
获取方法信息:
Method getMethod(String name, Class[] params) //使用特定的参数类型,获得命名的公共方法
Method[] getMethods() //获得类的所有公共方法 ,可获得父类的方法。
Method getDeclaredMethod(String name, Class[] params) //使用特写的参数类型,获得类声明的命名的方法
Method[] getDeclaredMethods() //获得类声明的所有方法 -
创建Class对象:
- 对象.
getClass();
类.class
Class.forName(“包名.类名”)
静态方法
- 对象.
-
反射API调用方法
-
通过Class对象获得实例化对象:
-
class对象.newInstance()
必须存在一个无参的构造方法,底层调用的是无参的构造器。否则需要找到一个构造方法去调用:Class clazz = Class.forName(" ");
Object O = clazz.newInstance(); -
通过
getConstructor(String.class,int.class);
获得构造器,然后con.newInstance(String,int):
Class clazz = Class.forName(" ");
Constructor con = clazz.getConstructor(int.class);
Object O = con.newInstanceof(1);
-
-
Class对象的
getMethod
需要传入方法名和参数类型的Class对象引用:确保找到唯一匹配的方法public Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {}
method.invoke(obj,args)
传入对象参数,保证执行方法时知道是哪个对象的数据,如果是静态方法就不需要。
-
Class对象获得属性
-
-
反射创建数组
//通过Array.newInstance()创建数组对象。
public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException {
return newArray(componentType, length);
}public static void TestAraary() Throws ClassNotFoundException{
Class<?> clazz = Class.forName("java.lang.String");//创建String的Class对象。
Object array = Array.newInstance(clazz,25);//创建Array对象。
Array.set(array,0,"a");
Array.set(array,0,"b");
Array.set(array,0,"c");
Array.set(array,0,"d");
//添加数组成员
//获取数组成员
Array.get(array,0);
Array.get(array,1);
}
//set 与 get方法都是native方法 (C/C++实现的)
-
-
-
-
序列化:就是把对象通过流的方式存储到文件中:可以是网络文件或磁盘设备。此对象 要重写Serializable 接口才能被序列化。
-
永久保存数据
-
只有实现了
Serializable和Externalizable接口
的类的对象才能被序列化,不想序列化的属性可以加上,transient
关键字。 -
java.io.ObjectOutputStream
代表对象输出流,它的writeObject(Object obj)
方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。 -
步骤:
- 1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
- 2) 通过对象输出流的writeObject()方法写对象。
-
反序列化步骤:
- 1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
- 2) 通过对象输入流的readObject()方法读取对象。
class Person implements Serializable {
private int age;
private String name;
private String sex;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
}
public class test(){
public static void main(){
//序列化:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File("test.txt")));
out.writeObject(person);
System.out.println("Person对象序列化成功!");
out.close();
//反序列化:
ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("test.txt")));
Person person = in.readObject();
System.out.println(person.getName());
System.out.println("Person对象反序列化成功!");
}
}
-
-
克隆:clone方法来源于java中object类,在jdk中的解释为:该方法返回一个此对象的副本。clone顾名思义就是复制的意思。所谓复制对象就是在内存中分配一个和原一模一样的空间,在此创建新的对象。内容一致但是指向的内存空间不同。由于Object类中clone方法是protected 修饰的,所以我们必须在需要克隆的类中重写克隆方法。
package Coding;
/* 深复制 ,在对clone方法进行重写时手动对要复制的对象的引用进行复制*/
public class Clone implements Cloneable{
class Head implements Cloneable{
public Head(){
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public Head head = new Head();
protected Object clone() throws CloneNotSupportedException {
Clone new_clone = (Clone) super.clone();
new_clone.head = (Head) head.clone();
return new_clone;
}
public static void main(String[] args) throws CloneNotSupportedException {
Clone C = new Clone();
Clone C1 = C;
if(C1 == C ){
System.out.println("两对象指向同一地址");
}
else {
System.out.println("两对象指向不同地址");
}
Clone C2 = (Clone) C.clone();
if(C2 == C){
System.out.println("两对象指向同一地址");
}
else {
System.out.println("两对象指向不同地址");
}
System.out.println(C.head);
System.out.println(C2.head);
}
} -
IOC:容器创建对象。
- DI:依赖注入实现IOC技术:通过名字从容器中拿到对象,对象属性赋值
- 在程序中提供使用的对象的名称,对象的创建,赋值,查找由容器管理。
-
Spring使用DI实现IOC功能,底层使用反射机制。
-
使用Spring创建对象
-
创建maven
-
加入maven依赖->spring依赖
-
创建类(接口和他的实现类)
-
创建Spring需要使用的配置文件:声明类,这些类由Spring创建,管理
<!--
beans:根标签,spring把java对象成为bean
spring-beans.xsd为约束文件
-->
<!--
声明bean,告诉Spring创建某个类对象
两个属性:
id:对象的自定义名称,唯一值。通过此名称找到对象
class:类的全限定名称,不能为接口,Spring通过反射机制创建类。
spring通过反射机制完成对象创建
Spring将创建好的对象放入map中:springmap.put(id,对象)
-->
<bean id="myservice" class="com.example.Ser.service"> -
获取Spring创建的对象
-
指定spring配置文件名:
String name
-
通过pp
ApplicationContext
接口创建实现类获取对象。Application spring = new Application();
//两个的实现类:
new FileSystemXmlApplicationContext() //从磁盘中读配置文件,不常用
new ClassPathXmlApplicationContext() //配置文件在当前项目中
//从类路径中加载配置文件,mvn时会讲resources路径下额配置文件拷贝到target下
ApplicationContext cont = new ClassPathXmlApplicationContext(config);
//例如:
public static void main(String[] args )
{
String config = "bean.xml"; //加载配置文件
ApplicationContext cont = new ClassPathXmlApplicationContext(config);//创建spring容器,读取配置文件,并创建所有对象。
serviceimpl serv1 = (serviceimpl) cont.getBean("myservice"); //获取对象
serv1.doThins(); //调用方法
}
-
-
获取容器中对象的信息:
String config = "bean.xml";
ApplicationContext cont = new ClassPathXmlApplicationContext(config);
//创建对象:默认调用无参构造方法函数返回的时Object对象,因此需要向下转型:强制转换
serviceimpl serv1 = (serviceimpl) cont.getBean("myservice");
//获取容器中对象的数量:
int cont.getBeanDefinitionCount();
//获取容器中对象的名称
String []names = cont.getBeanDefinitionNames();
-
-
-
Spring中给对象属性赋值
-
基于XML的DI
-
set注入(设置注入):spring调用类的set方法实现赋值
<!--简单类型:基本数据类型和String-->
<!--spring只会去找name值的set方法,和有无此属性无关-->
<bean id = "student_1" class = "org.example.DI_CREATE.Student" >
<property name = "age" value = "20" />
<property name = "matt" value = "21" />
</bean>
<!--引用类型:使用ref指定引用类型的对象名,在使用bean创建对象 -->
<bean id = "student_1" class = "org.example.DI_SET.Student">
<property name = "age" value = "19" /> <!-- 执行setage()方法-->
<property name = "name" value = "matt"/> <!--执行setname()方法-->
<property name="school" ref="school1" />
</bean>
<bean id="school1" class ="org.example.DI_SET.School">
<property name="name" value="电子科技大学"/>
<property name="addr" value="成都市成华区"/>
</bean> -
构造注入:spring调用类的构造方法实现赋值
<!-- 默认调用的时无参构造创建对象 -->
<!-- 调用构造方法进行属性赋值:使用constructor-arg标签
name:构造方法形参名
index:(参数索引值)构造方法参数位置,从0开始
value:参数值
-->
<bean id = "student_1" class = "org.example.DI_SET.Student">
<constructor-arg name = "age" value = "19" /> <!-- 执行构造方法-->
<constructor-arg name = "name" value = "matt"/> <!--执行构造方法-->
<constructor-arg name="school" ref="school1" />
</bean>
<bean id="school1" class ="org.example.DI_SET.School">
<constructor-arg name="name" value="电子科技大学"/>
<constructor-arg name="addr" value="成都市成华区"/>
</bean> -
引用类型:自动注入
-
ByName:按名称注入,将配置文件中bean标签id和类中引用类型属性名一致,且数据类型相同,既可自动注入
<bean id = "student_1" class = "org.example.DI_ByName.Student" autowire="byName">
<property name="name" value="matt"/>
<property name="age" value="19"/>
</bean>
<bean id="school" class ="org.example.DI_ByName.School" >
<property name="name" value="UESTC"/>
<property name="addr" value="成华大道建设路"/>
</bean>
<!-- spring自动将和Student类中同名的引用类型属性名school 注入 --> -
Bytype:按类型注入,类中引用类型和spring容器中bean标签的class属性是同源关系(相同或继承或接口实现类关系)
<bean id = "student_1" class = "org.example.DI_ByName.Student" autowire="byType">
<property name="name" value="matt"/>
<property name="age" value="19"/>
</bean>
<bean id="school_1" class ="org.example.DI_ByName.School" >
<property name="name" value="UESTC"/>
<property name="addr" value="成华大道建设路"/>
</bean>
<!-- 多个同源关系会报错:没有唯一的匹配 --> -
-
多配置文件分类
-
按功能模块分:
- 主模块配置文件没有对象,用来指定其他配置文件的位置:
<import resource="classpath:classes 文件下配置文件路径"/>
<!--使用通配符:文件在同一级目录。注意不要把自己文件加载进去,防止死循环 -->
<import resource="classpath:classes *.xml"/> -
按类的功能类:数据库/事务/service
-
-
常用对象:dao类,service类,contoller类,工具类
-
不放入Spring:实体类对象,servlet,listener,filter。
-
-
基于注解的DI:使用的是spring-context依赖中的spring-aop依赖。在spring配置文件中,加入一个组件扫描器标签,说明注解在项目中的位置。
-
@Component
:/*
@Component注解:创建对象,功能类似于<bean>
属性:value为对象的名称:bean中id的值
value的值唯一的,整个spring容器中就一个
位置:在类的上面
*/
/*
其他用法:
不指定value值,由spring指定默认的值:类名只有首字母大写的话变成小写/其他不变
*/
public class Student {
private String name;
private int age;
public Student(){
this.name = "vin";
this.age = 18;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}配置文件中:添加组件扫描器
<!--组件扫描器:组件:java对象
base_package:指定注解在项目中的包名
组件扫描器工作方式:扫描base-package指定的包,以及包中和子包中所有的类,找到类中的注解,按照注解创建对象或给属性赋值
加入component-scan标签后:配置文件:
1. 加入了一个新的约束文件spring-contex.xsd
2. 给这个新的约束文件起个新的命名空间:http://www.springframework.org/schema/context,指定一个url的约束文件xsd地址
再通过一个标签context指定此命名空间
-->
<beans>
<context:component-scan base-package="org.example.Component"/>
</beans> -
@Respository
/* 持久层
放在dao的实现类上面,表示创建dao对象,dao对象访问数据库
*/@Service
/*
用在业务层,放在service实现类上面
创建service对象,service对象做业务处理,可以有事物等功能。
*/@Controller
/*
用在控制器上面
创建控制器对象,接受用户提交的参数,显示请求的处理结果。servlet功能。
*/以上三个注解语法与component一样创建对象。但也有额外的功能,用于给项目对象分层。
-
扫描多个包
- 使用多个组件扫描器
- 使用分隔符;或者,分隔多个包名
- 指定父包:递归扫描
-
@Value
<!-- 简单类型赋值 -->
/*
@Value:简单类型的属性赋值
属性:value:String类型,表示简单类型的属性值,反射原理。
位置:直接再属性的上面,无需set方法:推荐使用
在set方法之上
*/
public class Student {
private String name;
private int age;
public Student(){
this.name = "vin";
this.age = 18;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}@Autowired
/*
@Autowired:spring实现引用类型的赋值
属性:require = true:表示引用类型赋值失败,程序报错终止执行。
require = false :引用类型赋值失败,则赋值为null,程序不终止。
使用的是自动注入方法,支持,ByName,ByType
默认使用的是ByType
使用ByName方式:
@Autowired
@Qualifier("school_1")
*/Resource
/*
@Resource来自JDK的注解,spring提供支持,可以给引用类型赋值
给引用类型赋值,应用类型变量上或set方法
默认ByName方式:先使用ByName,失败再使用ByType
只是用ByName方式:
@Resource(name="")
*/使用自定义属性变量:${}
//引入自定义属性变量文件
<context:property-placeholder location="classpath:my.properties"/> -
-
IOC实现了解耦合:业务对象之间的解耦合:service和dao和controller
-
-
AOP:面向切面编程
-
代理:基于反射机制。功能增强,控制访问
-
静态代理:代理类是手工实现;目标类是确定的。设计模式中:代理模式
-
用户不能直接访问目标类中方法实现功能,而代理类可以且必须。
-
缺点:目标类增多时,代理类数量会增加
接口中功能增加。修改,会影响实现类:目标类与代理类。
-
-
动态代理:目标类很多,代理类数量可以很少, 修改接口后,不回影响代理类。
-
JDK动态代理:在程序运行过程中,通过反射机制,直接创建代理对象,动态指定代理目标。
-
目标对象必须为接口,目标类与代理类实现相同的接口。java.lang.reflect包提供三个类支持代理模式:Proxy,Method和InvocationHandler
-
InvocationHanler接口:
invoke()
方法:代理类完成的功能写在此方法中。public Object invoke(Object proxy,Method method,Object[] args)
/*
参数依次为:
proxy:代理类对象,无需赋值:使用静态方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interface,InvocationHandler h)
目标对象的类加载器
接口,目标对象实现的接口
InvocationHandler :自己写的代理类
Method:目标类中方法的Method对象,jdk提供,执行的反射中的
public Object Method.invoke(Object obj,Object[] args)
args:目标类方法参数,jdk提供
*/
/*
创建接口实现类
重写invoke方法实现代理类中的功能。
*/ -
使用步骤
-
创建InvocationHandler实现类,重写invoke方法:使用反射的Method.invoke()执行目标类中方法,需要动态传入目标类对象。
-
使用:创建目标类对象
创建InvocationHandler对象,传入目标类对象。
创建Proxy对象:通过静态方法newProxyInstance(ClassLoader loader,Class<?>[] interface,InvocationHandler h),转型为接口类型
-
通过代理执行接口中方法。
-
使用场景:
- 系统中存在的类修改功能:增加功能。
- 给项目中多个类增加相同的功能。
- 给业务方法增加事物,日志输出
-
优点:
- 改变接口不会影响代理类,直接通过代理对象带调用想要调用的方法。
- 不用写许多代理类,而是通过Proxy中静态方法创建创建代理对象。
- 不改变源码的情况下增加功能
- 减少代码重复
- 解耦合
- 专注业务逻辑
package Coding.Dynamic_Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface FindJob{
public void find();
}
//目标类
class Person implements FindJob {
public void find(){
System.out.println("hand in resume");
}
}
class My_Proxy implements InvocationHandler{
//通过构造方法传入目标对象
private Object object;
public My_Proxy(Object object){
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("accept proxy");
method.invoke(object,args);
System.out.println("proxy answer");
System.out.println("proxy end");
return null;
}
}
public class Dynamic_Proxy {
public static void main(String[] args) {
Person person = new Person();
InvocationHandler handler = new My_Proxy(person);
FindJob findJob = (FindJob) Proxy.newProxyInstance(person.getClass().getClassLoader(),person.getClass().getInterfaces(),handler);
findJob.find();
}
}
-
-
-
-
CGLIB动态代理:第三方工具库。无接口的类实现动态代理:通过继承的方式重写父类的方法实现代理。因此目标类和方法不能是final。
-
-
AOP:底层使用动态代理,两种动态代理都可以使用。动态代理的规范化,用一种统一的方式,使用动态代理。
- Aspect:切面:给目标类增加的功能。
- 一般是非业务方法。
- 常见切面功能:日志,事物,统计信息,参数检查,权限验证。
- JoinPoint:连接点,连接业务和和切面的位置。某类中的业务方法。
- Pointcut:切入点,指多个连接点方法的集合。多个方法。切面执行的位置
- 目标对象:给哪个类增加功能。
- Advice:通知,表示切面功能执行的时间
- Aspect:切面:给目标类增加的功能。
-
AOP的实现:spring框架,在事物处理时使用aop,但是比较笨重。常用的是aspectJ:一个开源的aop框架。spring集成了aspectJ的功能:
-
使用xml方式。
-
使用注解,一般使用注解。
-
切面的执行时间:Advice(通知,增强)
-
在aspectJ中使用注解表示,也可以使用xml标签。
//前置通知 :
-
-
切面的执行位置:Pointcut使用切入点表达式
execution(modiduers-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:抛出异常类型
?:表示可选部分
execution(访问权限 方法返回值 包名 方法声明(参数列表) 异常抛出类型)
必有得:方法返回值,方法声明。-
模式可以使用通配符:
-
*0到多个字符
-
…:方法参数中,可变参数
包名之后:当前包及其子包
-
+:类名之后:表示当前类及其子类
接口之后:表示当前接口及其实现类
-
-
举例:
execution(public * *(..))//所有公共方法
execution(* set*(..)) //所有的set方法,省略了权限控制符
execution(* com.xyz.service.*.*(..))//service包中所有类中的所有方法
execution(* com.xyz.service..*.*(..))//service下所有子包的所有类的所有方法
execution(* *..service.*.*(..)) //任意级service包中所有类的所有方法-
步骤:
-
加入Aspect依赖
-
创建目标类和其接口:方法为void类型方法
-
创建切面类:普通类
-
在类上加入@Aspect
-
类中定义方法,切面的执行功能
-
在方法上面加入aspectj的通知注解有需要指定切入点表达式指定需要增强的目标类。
-
:前置通知@Before注解
/*
方法定义要求
公共方法
没有返回值
有参数的话不能自定义:选择参数Joinpoint:代表业务方法,加入切面功能的方法
作用:在通知方法中获取方法执行时的信息,方法名称与实参
有框架赋值,必须是第一个位置的参数
*/
public class Aspect_1 {
public void func_1(JoinPoint joinPoint){
System.out.println("方法的签名:" + joinPoint.getSignature());
System.out.println("方法的名称:" + joinPoint.getSignature().getName());
Object[] args = joinPoint.getArgs();
for(Object arg:args){
System.out.println("方法的参数:" + arg);
}
System.out.println("在目标类前完成日志:");
System.out.println("日期:" + new Date());
}
} -
后置通知@AfterReturning注解
package org.example.Aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
public class Aspect_after {
/*
两个属性:
value:切入点表达式
returning:自定义变量,用于存放目标方法的返回值
变量名必须与下面的通知方法形参名一致。
因此可以捕获到方法的返回值,也可以修改
执行过程:
Object res = do_other();
after_func(res);
*/
public void after_func(Object res){
/*
此res形参为方法的返回值,与注解中returning 值相同
*/
System.out.println("后置通知执行,获取的返回值为:"+res);
res = -1;
}
} -
-
@Around注解:
package org.example.Aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.Date;
public class Aspect_around {
/*
public
必须有一个返回值
固定参数ProceedingJoinpoint:用于执行目标方法。
1. 功能最强的,在前后都执行
2. 控制目标方法是否被调用执行
3. 可以修改原来的执行结果
4. 等价于JDK动态代理:InvocationHandler
5. 可以直接通过ProceedingJoinPoint(继承于JointPoint接口)获取参数的值
6. 常做事物,在目标方法前开启事务,目标方法执行完提交事物。
*/
public Object func_around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object res;
Object []args = proceedingJoinPoint.getArgs();
String name =(String) args[0];
System.out.println("====目标方法前时间:=====" + new Date());
if("matt".equals(name)) {
res = proceedingJoinPoint.proceed();
}
System.out.println("====目标方法后提交:=====" + new Date());
return "suceess_around";
}
}- 异常通知@AtfterThrowing
package org.example.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
public class Aspect_afterthrowing {
/*
value:切入点表达式
throwing:目标方法抛出的异常对象,于通知方法中异常的参数名相同。
作用:
1.做监控程序,监控目标方法是否有异常
有异常可以做其他事务:邮件,消息
*/
/*
无返回值
参数Exception,或者JointPoint
*/
public void atferthrwoing_func(Exception e){
System.out.println("捕获到异常:" + e.getMessage() );
}
}- 最终通知@After
package org.example.Aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
public class Aspect_After {
/*
value:切入点表达式
特点:总是执行,在目标方法后执行,一般做资源释放工作
*/
/*
无参数通知
参数可为:JoinPoint
*/
public void After_func(){
System.out.println("执行最终通知");
}
}- 辅助注解@Pointcut
package org.example.Aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import java.util.Date;
/*
@Pointcut:管理多个相同的切入点表达式
使用:在一个通知方法上面使用@Pointcut注解
指定value切入点表达式的值,则此通知方法就可以作为切入点表达式的别名。
*/
public class Aspect_PointCut {
private void pointcut_1(){
}
public void After_func(){
System.out.println("执行最终通知");
}
public void beforen_func(JoinPoint joinPoint){
System.out.println("方法的签名:" + joinPoint.getSignature());
System.out.println("方法的名称:" + joinPoint.getSignature().getName());
Object[] args = joinPoint.getArgs();
for(Object arg:args){
System.out.println("方法的参数:" + arg);
}
System.out.println("在目标类前完成日志:");
System.out.println("日期:" + new Date());
}
}-
配置spring配置文件
-
声明对象,把对象交给容器管理
- 目标对象
- 切面对象
- aspectJ框架中的自动代理生成器标签
- 自动代理生成器:完成代理对象的自动创建功能
<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
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id = "service_1" class= "org.example.ServiceImp.Serviceimp"/>
<bean id = "aspect_1" class = "org.example.Aspect.Aspect_1"/>
<aop:aspectj-autoproxy/>
</beans>
-
-
从spring容器中获取目标对象,强转为接口类型(多态)。
/*
test方法
*/
package org.example;
import static org.junit.Assert.assertTrue;
import org.example.Service.Service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Unit test for simple App.
*/
public class AppTest
{
/**
* Rigorous Test :-)
*/
public void test_1()
{
String config = "applicationContext.xml";
ApplicationContext con = new ClassPathXmlApplicationContext(config);
Service service =(Service) con.getBean("service_1");
System.out.println(service.getClass().getName());
service.do_service("matt",19);
}
} -
-
目标类没有接口:通过CGLIB实现动态代理
//目标类:
package org.example.Service;
public class Service_1 {
public void CGLIB_func(){
System.out.println("===执行无接口的类方法===");
}
}
//切面
package org.example.Aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
public class Aspect_CGLIB {
public Object fun_CGLIB(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("通过CGLIB执行动态代理:" );
proceedingJoinPoint.proceed();
System.out.println("完成CGLIB执行动态代理:" );
return "success";
}
} -
有接口也可以使用CGLIB事项动态代理:在配置文件中指明
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--可能效率高点-->
-
-
-
-
Spring集成Mybatis‘
- 使用ioc:通过ioc创建Mybatis中的对象,由Spring容器进行管理。
-
Web中使用Spring对象。
-
SpringMVC:基于spring的框架:实际就是spring的一个模块:可以使用AOP和IOC ,可以理解为servlet的一个升级。
-
三层架构:
- 第一层:SpringMVC:界面层:接受请求,显示处理结果
- 第二层:Spring,业务层:处理业务逻辑,Spring创建service,dao,工具类等对象。
- 第三层:MyBatis,持久层,访问数据库。
-
springmvc容器中放控制器对象,使用@Controller注解创建的是一个普通的对象,不是Servlet对象。springMVC赋予了控制器对象额外的功能。
-
web开发底层是servlet,springmvc中有一个对象是Servlet:DispatherServlet负责接收用户的所有请求,之后DispatcherServlet把请求转发给Controller对象。
- index.jsp-------------->DispatcherServlet(Servlet对象)----------转发,分配--------->Controller对象(@Controller注解创建的对象)。
-
@Controller创建处理器对象,@Service创建业务对象,@Autowired或@Resource在控制器类中注入Service,Service类中注入Dao。
-
步骤
-
加入spring-webmvc依赖,间接加入spring依赖
-
加入servlet依赖以及前端页面jsp等依赖
-
重点:在web.xml中注册springmvc框架的核心对象DispatherServlet
-
DispatherServlet也叫中央调度器,是一个Servlet,继承于HttpServlet
-
作用
- 创建WebApplicationContext对象,指定读取配置spring配置文件,创建控制器对象
- 是一个Servlet对象,通过Servlet-Mapping接收用户请求并进行调度
-
也叫前端控制器
-
负责接受用户的请求,调用其他的控制器对象,请求处理结果显示给用户
-
在web.xml中通过Servlet-Mapping配置请求映射,将不同类的请求给指定的中央调度器,中央调度器通过@Controller对象中@RequestMapping指定的请求方法完成请求并返回数据。
<!--web.xml文件-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--声明注册核心对象DispatcherServlet
在tomcat服务器启动后就创建对象实例。
在创建DispatcherServlet对象时,会同时创建springmvc容器对象
读取sprinmvc配置文件,把所有对象都创建好,在请求时就可以直接使用。
DisapatcherServlet的init()方法:
创建容器读取配置文件:
WebApplicationContext cont = new ClassPathXmlApplicationContext("springmvc.xml");
把容器对象放到ServletContext中
getServlet().setAttribute(key,cont);
-->
<servlet>
<servlet-name>springmvc_controller</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--在启动时,创建Servlet对象
load-on-starup标签:表示在启动后创建对象的顺序。值为整数,值越小,tomcat创建对象得时间越早。
默认读取的配置文件位置:/WEB-INF/<servlet-name>-servlet.xml
自定义mvc读取配置文件的位置L:
-->
<init-param>
<!--springmvc配置文件的位置属性:-->
<param-name>contextConfigLocation</param-name>
<!--指定自定义文件的位置-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc_controller</servlet-name>
<!-- 两种url -pattern的值
1.使用扩展名,语法:*.xxx,xxx为自定义扩展名。常用*.do,*.action,*.mvc等
如:http://localhsot:8080/springmvc_controller/service.do
2.使用斜杠"/"
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app> -
-
创建请求页面:
<%--
Created by IntelliJ IDEA.
User: OMEN
Date: 2021/7/20
Time: 21:04
To change this template use File | Settings | File Templates.
--%>
<%-- 通过<a href> 指定链接请求的地址--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>测试请求</p>
<p><a href="service.do">发起service.do请求</a></p>
</body>
</html> -
创建控制类
-
类上加入注解@Controller,创建对象,放到springmvc容器中
-
在类中的方法上面加入RequestMapping注解:请求地址与方法的映射。类上:指定类中所有方法的模块:/test/*.do
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/*
创建控制器对象
后端控制器(处理器)
*/
public class controller_1 {
/*
通过方法处理用户提交的请求,springmvc中使用的是方法来处理
方法是自定义的,可以有多种返回值,多种参数,方法名自定义
*/
/*
@RequestionMapping:请求映射,将一个请求于一个方法绑定在一起
一个请求指定一个方法处理
属性:1. value:请求得url地址,必须唯一,使用时,推荐地址以“/”开头
说明:使用@RequestionMapping修饰得方法叫做处理器方法或者控制器方法,可以处理请求,类似于Servlet得doGet,doPost
*/
/*
返回值ModelAndView
Model:数据,请求处理完后显示给用户的数据
View:视图:比如jsp等等
*/
//处理页面的请求地址
// @ResponseBody
public ModelAndView doService(){
System.out.println("进入控制器");
// 先简单返回数据
ModelAndView modelAndView = new ModelAndView();
//添加数据,框架在请求的最后把数据放入request作用域
//request.setAttribute("msg","返回的图片为:")
modelAndView.addObject("msg","返回的图片为:");
modelAndView.addObject("func","执行的方法:");
//指定视图,视图的完整路径
//框架对视图执行dorward操作,request.getRequestDispatcher("/show.jsp").forward(...)
modelAndView.setViewName("show.jsp");
System.out.println("退出控制器");
return modelAndView;
//后期做setAttribute何forward处理
}
} -
-
创建结果页面
-
创建springmvc配置文件(spring配置文件)
-
声明组件扫描器,用于指定**@Controller注解**所在的包名
-
声明视图解析器:处理视图:
- 将结果页面放在保护路径中,不可直接访问。用视图解析器:
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--声明组件扫描器-->
<context:component-scan base-package="com.example.controller"/>
<!--视图解析器:指定视图的文件的路径,prefix:前缀路径,前后都有“/”,suffix:后缀名。-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>//配置视图解析器后只需要文件名:逻辑名指定页面
modelAndView.setViewName("show");
-
-
-
请求处理过程:
- 前端页面请求service.do
- tomcat,通过web.xml的servlet-mapping给对应的中央调度器
- 中央调度器根据配置的spring配置文件中的Controller对象中的RequestMapping指定的方法处理请求。
- Contoller对象经得到的ModelAndView转发给前端进行展示
-
注解开发
-
@RequestMapping
/*
属性:method,表示请求的方式。他的值是RequestMethod类枚举类型
如:RequestMethod.GET,RequestMethodMeThod.POST
get可用:<a href=""/>
<br/>
post:<form action=""/ method="post">
<input type="submit" value="提交">
</form>
*/ -
接受参数:放在处理器方法中的形式参数,用于接受用户的参数
HttpServletRequest
HttpServletResponse
HttpSession -
请求中的参数
-
逐个接受
-
请求jsp:
<%--
Created by IntelliJ IDEA.
User: OMEN
Date: 2021/7/20
Time: 21:04
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>提交参数</p>
<form action="picture/first.do" method="post">
姓名:<input type="text" name = "name">
账号:<input type="text" name="account">
密码:<input type="text" name="psw">
<input type="submit" value="提交">
</form>
</body>
</html> -
控制器接收:
/*
使用@RequestePara解决请求中参数和处理器方法中形式参数名不相同
value属性值:为请求中的参数名
位置放在形式参数定义前
require属性:默认为true,请求中必须含有西参数,(url中get请求)无参数会报错
为false,可以为空。
*/package com.example.controller;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.net.http.HttpResponse;
public class controller_1 {
/*
接收参数过程
1. 使用Request对象接收
String name = request.getParaMeter("name");
String age = request.getParaMeter("age");
2.mvc框架通过中央调度器调用Controller对象的doSome方法
此时按照请求的参数名与形参名对应进行传值,并进行类型转换。
3.如果提交要转换为int数据时填空字符串,字母等,则会发生400错误:客户端异常Integer.valueof(String str)
*/
public ModelAndView doService(){
System.out.println("=====进入控制器=====");
ModelAndView modelAndView = new ModelAndView();
System.out.println("=====调用的service方法处理图片=====");
modelAndView.addObject("msg","返回的图片为:");
modelAndView.addObject("func","执行get请求的方法:");
modelAndView.setViewName("show");
System.out.println("=====退出控制器=====");
return modelAndView;
}
public ModelAndView first(String name,int age,String account,String psw){
System.out.println("=====进入控制器=====");
ModelAndView modelAndView = new ModelAndView();
System.out.println("=====调用的service方法处理图片=====");
modelAndView.addObject("name",name);
modelAndView.addObject("age",age);
modelAndView.addObject("account",account);
modelAndView.addObject("psw",psw);
modelAndView.setViewName("show");
System.out.println("=====退出控制器=====");
return modelAndView;
}
}<!--4.post方式中文乱码:解决
设置HtppServletRequest h参数并在控制器代码中指明编码方式
h.setCharEncoding("utf-8")
web.xml文件中设置过滤器:自定义或者自定义 -->
<!--声明过滤器-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--设置属性参数-->
<!--设置HttpServletRequest对象使用的编码值-->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<!--强制使HttpServletRequest对象使用上面编码-->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<!--/*表示所有的请求通过过滤器进行过滤-->
<url-pattern>/*</url-pattern>
<servlet-name>springmvc_controller</servlet-name>
</filter-mapping>
-
-
对象参数接受:接收多个参数
-
优点一个对象参数接收多个参数,缺点,名字必须一致,且要有set,get方法。
package com.example.controller;
/*
对象接收参数,属性名与请求参数名一致
set方法与get方法
*/
public class Picture {
String name;
String age;
public Picture(){
System.out.println("创建picture对象节进行参数接收");
}
public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public String getAge() {
return age;
}
}处理器方法:
public ModelAndView first(Picture picture){
System.out.println("=====进入控制器=====");
ModelAndView modelAndView = new ModelAndView();
if("".equals(picture.name) ||"".equals(picture.age) ){
System.out.println("=====参数为空处理=====");
modelAndView.setViewName("show_400");
}
else {
System.out.println("=====调用的service方法处理图片=====");
modelAndView.addObject("name", picture.name);
modelAndView.addObject("age", picture.age);
modelAndView.addObject("picture",picture);
modelAndView.setViewName("show");
}
System.out.println("=====退出控制器=====");
return modelAndView;
} -
控制器方法返回值
-
ModelAndView:Model数据部分(最终在Request作用域里),View视图进行转发操作。同时用数据和视图
-
String:只进行页面跳转:逻辑名称或完全路径。
-
可以手动添加数据到Request域:
public String jump(HttpServletRequest httpServletRequest,String name,String age){
System.out.println("=====进入控制器=====");
httpServletRequest.setAttribute("name",name);
httpServletRequest.setAttribute("age",age);
System.out.println("=====调用的service方法处理图片=====");
System.out.println("=====退出控制器=====");
return "show";
}
-
-
void:不能表示数据,也不表示视图。处理ajax,可以使用void,通过HttpServletResponse输出数据,服务器只返回数据,和视图无关。
-
Object:响应Ajax,返回数据与视图无关。
-
步骤
-
加入处理json的工具库依赖
-
对象转成json:
<mvc:annotation-driven>
-
在处理器方法上加入@ResponseBody注解:进行输出
-
-
-
-
Tomcat可以处理静态资源:html,js,jsp等等,有一个默认Servlet,启动时创建
-
还可以处理未映射的请求。
-
自己的web.xml文件url-pattern为"/"时会替代tomcat的默认Servlet,中央处理器不会处理静态资源。
-
需要在springmvc配置文件中添加标签,创建处理静态资源的控制器,最终是转发给tomcat进行处理。** **转发给默认控制器处理。
<mvc:annotation-driven/>
<mvc:default-servlet-handler/> -
和RequestMapping有冲突,需要加入注解驱动解决
-
-
-
第二种处理静态资源。同样有冲突,加注解驱动
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
-
-
-
-