Spring5学习
spring下载地址
下载5.2.6版本
入门案例 通过Spring配置文件创建对象并获取
导入Spring5相关包
导入最核心的四个包
Bean, Core, Context, Expression
Java工程中导入jar
包
新建配置文件bean1.xml
,Spring的配置文件采用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">
<!-- 配置User对象创建-->
<bean id="user" class="com.atguigu.spring5.User"></bean>
</beans>
com.atguigu.spring
下新建类User.class
package com.atguigu.spring5;
/**
* @author ColdRain
*/
public class User {
public void add() {
System.out.println("add...");
}
}
com.atguigu.spring.testdemo
新建测试类TestSpring5.class
。
- 这里要先加载Spring的配置文件,通过
ApplicationContext
接口,因为配置文件在包类路径下所以使用了实现类ClassPathXmlApplicationContext
,后续会详细讲解; - 通过
ApplicationContext
的变量的getBean(id, class)
方法获取对象实例;
package com.atguigu.spring5.testdemo;
import com.atguigu.spring5.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class TestSpring5 {
@Test
public void testAdd() {
// 1.加载Spring配置文件, 在src文件夹下可以通过ClassPathXmlApplicationContext直接检查出配置文件
// 可选FileSystemXmlApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
// 2.获取配置创建的对象
// 通过bean文件中的id值获取
User user = context.getBean("user", User.class);
System.out.println(user);
user.add();
}
}
IOC容器
IoC( Inversion of Control)
使用对象时,由主动new产生对象转换为由外部(Spring提供的容器,IoC容器)提供对象,此过程中对象创建控制权由程序转移到外部。
Spring提供了一个容器,称为IoC容器,用来充当IoC思想的外部。
IoC容器负责对象的创建、初始化等一些列工作,被创建或被管理的对象在IoC容器中统称为Bean。
DI(Dependency Injection)
在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入。
目的:充分解耦
- 使用IoC容器管理bean
- 在IoC容器内讲有依赖关系的bean进行关系绑定;
最终效果:
- 使用对象时不仅可以直接IoC容器中获取,并且获取到bean已经绑定了的所有依赖关系。
1. 什么是IoC(Inversion of Control)控制反转
1)控制反转,将对象创建和对象之间的调用过程交给Spring管理;
2)使用IOC目的:为了耦合度降低;
举例:
通过工厂模式,添加中间工厂解耦UserService
和UserDao
IOC(BeanFactory
接口)
1.IOC思想基于IOC容器完成,底层就是对象工厂;
2.Spring提供IOC容器实现两个接口:
BeanFactory
:IOC容器基本实现,Spring内部才可以使用的接口,不提供给开发人员。
*加载配置文件时不会创建对象,再获取对象(使用)才会创建对象。ApplicationContext
:BeanFactory
接口的子接口,由开发人员使用。
*加载配置文件时就会进行对象的创建。
3.ApplicationContext
接口有实现类:
IOC操作Bean管理(基于xml
方式)
1.基于xml
方式创建
<!--配置User对象创建-->
<bean id="user" class = "com.atguigu.spring5.User"></bean>
1)在Spring配置文件中,使用<bean>
标签进行对象的创建,标签内添加对应的属性,例:
id
:唯一标识。(name
和id
类似,name
中可以使用特殊符号)class
:类的全路径(包类路径);
2)创建对象时,默认执行无参构造方法;
DI(Dependency Injection)依赖注入,即基于xml方式注入属性
1.使用set
方法注入
a.创建类,定义属性和对应的set
方法
package com.atguigu.spring5;
public class Book {
private String bname;
private String bauthor;
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
public void testBook() {
System.out.println(this.bname + "::" + this.bauthor);
}
}
b.在Spring配置文件中进行对象创建,并进行属性注入。
- 属性注入使用
<property name="属性名" value="属性值"></property>
格式 。
<?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="book" class="com.atguigu.spring5.Book">
<property name="bname" value="Pretty Beauty"></property>
<property name="bauthor" value="anonym"></property>
</bean>
</beans>
p名称空间注入(了解)
在头配置文件中加入配置xmlns:p="http://www.springframework.org/schema/p"
完整头配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
在进行属性注入时,可以使用p命名的形式,即
<property id="唯一标识" class="包类" p:属性名=“属性值”>
例:
<bean id="book" class="com.atguigu.spring5.Book" p:bname="Jane Eyre" p:bauthor="Charlotte"></bean>
2.使用 有参构造器 进行注入
在上面的bean1.xml
文件中加入如下代码,这里不再通过<property>
标签,使用<constructor-arg>
标签。
- 格式为
<constructor-arg name="属性名" value="属性值"></constructor-arg>
<bean id="orders" class="com.atguigu.spring5.Orders">
<constructor-arg name="oname" value="Computer"></constructor-arg>
<constructor-arg name="address" value="China"></constructor-arg>
</bean>
null与特殊符号
1.null
值注入,使用<null/>
<property name="address">
<null/>
</property>
2.特殊符号注入,使用转义或者 <![CDATA[值]]>
格式
<!--
属性值包含特殊符号
1 把<>进行转义为<,>
2 使用<![CDATA[值]]>
-->
<property name="address">
<value><![CDATA[<<南京>>]]></value>
</property>
注入属性-外部bean
这里想要实现在service中调用UserDao的方法
原始方法
1.我们在包路径下新建dao
、service
包;
2.在dao
下新建interface
(接口)UserDao
,和实现类UserDaoImpl
。
UserDao
:
package com.atguigu.spring5.dao;
public interface UserDao {
public void update();
}
UserDaoImpl
:
package com.atguigu.spring5.dao;
public class UserDaoImpl implements UserDao {
@Override
public void update() {
System.out.println("dao-update...");
}
}
在service
下新建UserService
类
package com.atguigu.spring5.service;
import com.atguigu.spring5.dao.UserDao;
import com.atguigu.spring5.dao.UserDaoImpl;
import org.junit.Test;
public class UserService {
@Test
public void add() {
System.out.println("service-add...");
UserDao userDao = new UserDaoImpl();
userDao.update();
}
}
通过外部bean注入
我们将在UserService
中进行声明的UserDao
变量,改为类的属性,并且设定set()
函数,用于将UserDao
对象传进来;
UserService
:
package com.atguigu.spring5.service;
import com.atguigu.spring5.dao.UserDao;
import com.atguigu.spring5.dao.UserDaoImpl;
import org.junit.Test;
public class UserService {
//类属性
private UserDao userDao;
//将UserDao对象传入的set()函数
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add() {
System.out.println("service-add...");
//直接调用类属性的UserDao对象的方法
userDao.update();
}
}
我们在bean中进行UserDao
与UserService
对象的建立,我们新建一个配置文件src/bean2.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="userService" class="com.atguigu.spring5.service.UserService">
<!--注入userDao对象
name:set函数的参数名
ref:通过id引用bean文件中创建的对象
-->
<property name="userDao" ref="userDao"></property>
</bean>
<!--bean在property标签的外部,这就是为什么叫外部bean注入-->
<bean id="userDao" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>
</beans>
这里UserService
直接使用了bean
文件通过set()
函数进行传入UserDao
对象,同样是使用<property>
设置传参,这里使用了新的属性ref
,引用bean
内声明对象的id,格式为:
<bean id="唯一标识,可以看成对象名" class="包类路径">
<property name="参数名" ref="bean内对象id"></property>
</bean>
注:这里<property>
标签引用的bean
对象在<property>
的外部,这就是为什么叫做外部bean注入属性!
最后新建测试类TestBean.class
package com.atguigu.spring5.testdemo;
import com.atguigu.spring5.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBean {
@Test
public void testAdd() {
//加载类文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
//通过bean获取建立的对象
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
执行成功
注入属性-外部bean 和 级联赋值
外部Bean
这里新建两个类Dept.class
Department部门类,Emp.class
Employee员工类;
Dept.class
package com.atguigu.spring5.bean;
//部门类
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
@Override
public String toString() {
return "Dept{" +
"dname='" + dname + '\'' +
'}';
}
}
Emp.class
package com.atguigu.spring5.bean;
//员工类
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门
private Dept dept;
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public void add() {
System.out.println(ename + "::" + gender + "::" + dept.toString());
}
}
这里通过bean3.xml
文件进行对象及属性配置
bean3.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注入属性-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<property name="ename" value="John"></property>
<property name="gender" value="male"></property>
<!--对象引用参数-->
<property name="dept">
<!--内部bean书写,声明建立对象-->
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<!--调用Dept类的Set()方法-->
<property name="dname" value="Security"></property>
</bean>
</property>
</bean>
</beans>
这里使用的内部bean注入属性的方式,格式为:
<bean id="唯一标识/对象名" class="包类路径">
<!--这里传参需要一个对象-->
<property name="set()参数名">
<!--内部bean,新建一个对象-->
<bean id="唯一标识/对象名" class="包类路径">
<!--这里可以套娃,如果对象有属性并且有set()函数可以继续使用<property>标签进行赋值-->
<property name="set()参数名" value="参数值"></property>
...
</bean>
</property>
</bean>
级联赋值
bean4.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注入属性-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<property name="ename" value="John"></property>
<property name="gender" value="male"></property>
<!--对象引用参数-->
<!--级联赋值-->
<property name="dept" ref="dept"></property>
<!--这里是将上面传入的dept对象,再获取出来对dept的dname属性进行再赋值,所以需要Emp的Dept属性有get()方法-->
<property name="dept.dname" value="Techology"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="Finance"></property>
</bean>
</beans>
这里注意需要再Emp.class
中对Dept
属性添加get()
方法!
package com.atguigu.spring5.bean;
//员工类
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门
private Dept dept;
...
public Dept getDept() {
return dept;
}
public void add() {
System.out.println(ename + "::" + gender + "::" + dept.toString());
}
}
这里是将上面传入的dept
对象,再获取出来对dept
的dname
属性进行再赋值,所以需要Emp,class
的Dept
属性有get()
方法。
注入属性-集合注入
使用bean
进行集合类属性的注入,首先在类里设置集合类属性,并设置set()
方法;
package com.atguigu.spring5.collectiontype;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Student {
// 1 数组类型属性
private String[] courses;
// 2 list集合类型属性
private List<String> list;
// 3 map集合类型属性
private Map<String, String> map;
// set集合
private Set<String> set;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void test() {
System.out.println(Arrays.toString(courses));
System.out.println(list);
System.out.println(map);
System.out.println(set);
}
}
之后在bean1xml
文件中进行配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1 集合类型属性注入-->
<bean id="student" class="com.atguigu.spring5.collectiontype.Student">
<!--数组类型属性注入-->
<property name="courses">
<!--可以使用<list>和<array>-->
<list>
<value>Java</value>
<value>Database</value>
</list>
</property>
<property name="list">
<array>
<value>John</value>
<value>Tom</value>
</array>
</property>
<property name="map">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<property name="set">
<set>
<value>Mysql</value>
<value>Redis</value>
</set>
</property>
</bean>
</beans>
集合类属性的配置在<property>
标签里加:
数组[]
和List<E>
:
<property name="属性名">
<list>/<array>
<value>值</value>
<value><值/value>
...
<!-- 若存入为对象引用 -->
<ref bean="beanid"></ref>
...
</list>/</array>
</property>
这里要提一句,若List要存入的是对象引用,那么可以使用<ref bean="beanid">
进行注入;
Map
:
<property name="属性名">
<map>
<entry key="KEY" value="VAL"></entry>
<entry key="KEY" value="VAL"></entry>
...
</map>
</property>
Set
:
<property name="属性名">
<set>
<value> 值</value>
<value> 值</value>
...
</set>
</property>
注入属性-集合提取为公共独立部分
上一部分内容讲了如何在属性标签<property>
中注入集合,如果集合会被多处用到,我们可以将其提取为独立公共的配置(有自己的id
);
1.修改头文件,添加了xmlns:util="http://www.springframework.org/schema/util"
, http://www.springframework.org/schema/util/spring-util.xsd
(在xsi:schemaLocation
中)。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
2.配置文件bean.xml
中,添加集合配置,使用<util:list id="唯一标识">
标签:
<util:list id="bookList">
<value>Thinking Java</value>
<value>Math</value>
<!-- 如果集合存储的为对象内容,则可以使用<ref>标签进行对象引用 -->
<ref bean="对象beanid"></ref>
</util:list>
<!-- 外部bean引用 -->
<bean id="book" class="com.atguigu.spring5.collectiontype.Book">
<property name="list" ref="bookList"></property>
</bean>
FactoryBean
我们之前使用的普通的bean
,在配置文件配置了什么类,返回的就是什么类的对象;
FactoryBean(工厂bean
):配置文件中定义bean
的类,与实际返回的类对象可以不一样,实际返回的类对象要看实现FactoryBean
接口的类中是如何定义的;
举例:
1.我们新建类MyBean.class
,并实现接口FactoryBean<E>
,FactoryBean接口
中有三个方法需要重写,这里我们主要写的是第一个方法getObject()
,用来定义返回对象的类型。这里我们定义为返回Course
类实例。
package com.atguigu.spring5.factorybean;
import com.atguigu.spring5.collectiontype.Course;
import org.springframework.beans.factory.FactoryBean;
public class MyBean implements FactoryBean<Course> {
//定义返回对象类型bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("BeanFactory");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
2.我们在配置文件中配置类实例,这里配置了类MyBean.class
;
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean">
</bean>
3.我们在代码通过bean
获取对象实例,虽然配置文件中写的是类MyBean
,但是我们实际获取的Course
类!
@Test
public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
Course myBean = context.getBean("myBean", Course.class);
System.out.println(myBean);
}
设置通过Bean获取的实例是单例还是多例
我们默认通过Bean获取多个对象是单例的,例:
bean.xml
:
<bean id="book" class="com.atguigu.spring5.collectiontype.Book">
<property name="list" ref="bookList"></property>
</bean>
@Test
public void testCollection2() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
Book book1 = context.getBean("book", Book.class);
Book book2 = context.getBean("book", Book.class);
System.out.println(book1);
System.out.println(book2);
}
结果获取的实例都是同一对象。
在Spring配置文件中,<bean>
标签的scope
属性可以设置单实例还是多实例。
scope
有两个属性值:
- 默认值:
singleton
表示单实例对象; prototype
表示多实例对象;
这里我们将上述代码修改为多实例:
<bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype">
<property name="list" ref="bookList"></property>
</bean>
获取到的对象为不同实例。
scope
设置为singleton
与prototype
区别
singleton
为单实例,prototype
为多实例;- 设置为
singleton
时,加载Spring配置文件时就会创建单实例对象。
设置为prototype
时,加载Spring配置文件时并不会创建对象,而是在调用getBean()
方法时创建多实例 对象;
xml自动装配
自动装配autowire
: 根据指定装配规则(属性名称byName
或 属性类型byType
),Spring自动将匹配的属性值进行注入。
格式:
<bean id="唯一标识" class="包类路径" autowire="byName/byType"
>
byName
:自动注入属性的bean id
要与类中属性名保持一致;byType
:根据属性的类型自动注入,但是如果存在多个相同类型的属性,则无法正常注入。
实例:
包类路径com.atguigu.spring5.autowire
下新建两个类Department.class
,Employee.class
:
package com.atguigu.spring5.autowire;
public class Department {
}
package com.atguigu.spring5.autowire;
public class Employee {
//beanid要与属性名称保持一致;
private Department department;
public void setDepartment(Department department) {
this.department = department;
}
@Override
public String toString() {
return "Employee{" +
"department=" + department +
'}';
}
}
配置文件bean.xml
中
<!--自动装配-->
<bean id="emp" class="com.atguigu.spring5.autowire.Employee" autowire="byType">
<!--手动装配:<property name="department" ref="dept"></property>-->
</bean>
<bean id="department" class="com.atguigu.spring5.autowire.Department"></bean>
xml配置外部属性文件
例,在Spring配置文件配置数据库信息;
首先导入druid.jar
至/lib
下,之后在project structure中添加druid
包;
1.我们可以在配置文件中直接配置数据库信息
<beans
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
2.通过引入外部文件进行配置
新建src\jdbc.properties
数据库配置文件:
prop.diverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.username=root
prop.password=root
这里要先加入新的头连接:
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
在配置文件中导入外部文件后,就可以直接引用文件中的配置值,格式为${文件配置名称}
<!--引入外部文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.username}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
注解方式配置bean实例对象
annotation注解:
1.注解是代码特殊标记,格式为@annotation(name=value, ...)
;
2.注解作用在类、方法、属性上面;
3.使用注解可简化xml
配置;
Spring针对Bean创建对象提供的注解:
@Component
@Service
@Controller
@Repository
上述四个注解的功能是一样的,只是写法不同,都可以用来创建bean
实例。
例:
引入依赖
在配置文件bean.xml
添加组件扫描,这里使用的是<context>
组件需要引入相应头文件。
<context:component-scan base-package="com.atguigu"></context:component-scan>
这里扫描的是com.atguigu
包下的所有注解,如果想要扫描多个包也可以通过,
分隔。
<!--扫描多个包,可一个用“,”隔开-->
<context:component-scan base-package="com.atguigu.spring5.dao, com.atguigu.spring5.service"></context:component-scan>
类上的注解格式为@Component/Controller/Repository/Service
,不添加(value="beanid")
默认beanid
为类的首字母小写名称。
注解实际上就相当于在配置文件中的<bean id="..." class="..."></bean>
我们在新建com.atguigu.spring5
下新建包dao, service
包service
下新建UserService.class
类
// @Component, @Controller,@Repository()都可
// @Service(value="userService")//相当于<bean id="userService" class="...">
@Repository
public class UserService {
public void add() {
System.out.println("service add...");
}
}
我们在测试类中获取bean
实例
public class TestSpring5Demo1 {
@Test
public void testService() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
}
获取成功
开启组件扫描细节配置
Spring配置文件中,可以对组件扫描进行细化配置,我们设置<context:component-scan>
中的user-default-filters="false"
后,可以自定义注解过滤器。
<context:include-filter>
只扫描定义的注解;
<context:component-scan base-package="com.atguigu" use-default-filters="true" >
<!-- include定义要扫描哪些注释,这里只扫描Controller注释 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<context:exclude-filter>
扫描配置中注解以外的所有注解;
<context:component-scan base-package="com.atguigu" use-default-filters="true" >
<!-- 扫描Repository以外的所有注解 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
注解方式实现属性注入
属性注入注释: 通过anotation
自动注入<property>
标签
1.引用对象属性,我们可以在类属性上添加如下注解,以实现自动注入属性对象:
@Autowired
:根据属性类型自动注入;@Qualifier(value="beanid")
:根据指定名称(bean id
)进行注入,使用时必须和@Autowired
连用;@Resource选加(name="beanid")
:既可以根据属性类型自动注入,也可以根据指定名称注入;
2.基本类型属性注入:@Value(value="注入值")
例,我们在dao
包新建接口UserDao
与实现类UserDaoImpl
。
UserDao
:
package com.atguigu.spring5.dao;
public interface UserDao {
public void add();
}
UserDaoImpl
package com.atguigu.spring5.dao;
import org.springframework.stereotype.Repository;
//可以主动设定beanid,也可以不设置采用默认beanid=userDaoImpl
@Repository(value="userDaoImpl1")
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("UserDaoImpl add ...");
}
}
UserService
的UserDao userDao
属性添加@Autowired
注释
package com.atguigu.spring5.service;
// @Component, @Controller,@Repository()都可
// @Service(value="userService")//相当于<bean id="userService" class="...">
@Service
public class UserService {
//定义dao类型属性
//不需要添加 set() 方法
//添加注入属性注解 @Autowired,通过类型自动注入
@Autowired
//也可以通过指定beanid注入
//@Autowired
//@Qualifier(value="userDaoImpl1")
//也可以通过
//@Resource 可以指定beanid + (name="userDaoImpl1")
private UserDao userDao;
//基本类型属性注入
@Value(value="Joey")
private String userName;
public void add() {
System.out.println("service add...");
System.out.println("userName + " + userName);
userDao.add();
}
}
测试:
public class TestSpring5Demo1 {
@Test
public void testService() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
}
成功注入UserDao userDao
,String useName
属性!
完全注解开发
通过注解@Configuration @ CompoenetScan(basePackages = {"包路径"})
实现配置文件的注解扫描
1.创建配置类,代替xml
配置文件。在com.atguigu.spring5.config
包下新建SpringConfig.class
类。
package com.atguigu.spring5.config;
@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
//相当于配置文件中的<context:component-scan base-package="com.atguigu"></context:component-scan>
public class SpringConfig {
}
2.编写测试类,测试类获取配置文件和之前也不一样。
需要通过类AnnotationConfigApplicationContext(配置类)
进行配置。
@Test
public void testService2() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
测试成功!
Bean的生命周期
1.通过类的无参构造器创建bean
实例;
2.通过类的set()
函数设置bean
实例对象的属性值<property>
和其他引用<ref>
;
初始化之前执行的方法:postProcessBeforeInitialization
; (需配置后置处理器BeanPostProcessor.class
)
3.调用bean
的初始化方法;(需要配置初始化方法<bean init-method="初始化方法">
)
初始化之后执行的方法:postProcessAfterInitialization
; (需配置后置处理器)
4.获取bean
的实例对象; ApplicationContext.getBean()
5.当容器关闭,调用bean
的销毁方法;<bean destroy-method="销毁方法">
Bean的生命周期主要为五个步骤,但是如果配置了后置处理器,在初始化方法前和初始化方法后增加了两个步骤,一共七个步骤。
后置处理器是全局生效的,配置好后对配置文件中所有Bean对象都生效!
实例演示:
在包路径com.atguigu.spring5.bean
新建类Orders.class
:
package com.atguigu.spring5.bean;
public class Orders {
//无参构造器
public Orders() {
System.out.println("Firstly, 执行无参数构造器");
}
//属性
private String oname;
//set()方法给bean实例注入属性或引用
public void setOname(String oname) {
this.oname = oname;
System.out.println("Secondly, 调用set()方法设置属性值");
}
//创建bean初始化方法
public void initMethod() {
System.out.println("Thirdly, 执行初始化方法");
}
//bean销毁方法
public void destoryMethod() {
System.out.println("Fivethly,销毁bean对象");
}
}
配置后置处理器,包路径com.atguigu.spring5.bean
下新建MyBeanPost.class
,并实现BeanPostProcessor
接口,重写其中的两个方法postProcessBeforeInitialization()
, postProcessAfterInitialization()
。
package com.atguigu.spring5.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization:在初始化之前执行");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization:在初始化之后执行");
return bean;
}
}
配置src/bean4.xml
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- bean对象配置 -->
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destoryMethod">
<property name="oname" value="Phone"></property>
</bean>
<!-- 后置处理器,全局bean生效 -->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
</beans>
在测试类TestSpringDemo.class
中,测试bean对象的生命周期
@Test
public void test4() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("Fourthly,获取bean对象");
System.out.println(orders);
//手动销毁bean实例
((ClassPathXmlApplicationContext)context).close();
}
效果:
AOP
动态代理
AOP底层使用动态代理
有两种情况使用动态代理:
-
有接口情况,使用JDK动态代理 :
创建接口实现类对象,增强类的方法。
-
没有接口情况,使用CGLIB动态代理 :
创建子类的代理对象,增强类的方法。
AOP使用JDK动态代理实例
我们有一个接口interfaceUserDao
package com.atguigu.spring5;
public interface UserDao {
public int add(int a, int b);
}
类UserDaoImpl.class
实现UserDao
package com.atguigu.spring5;
public class UserDaoImpl implements UserDao {
@Override
public int add(int a, int b) {
System.out.println("adding");
return a + b;
}
}
我们要对接口实现类UserDaoImpl.class
的add()
方法实现增强。
创建接口实现类UserDaoImpl
代理类JDKProxy.class
在代理类中,我们要对实现类的方法进行加强,我们要通过Proxy.newProxyInstance(ClassLoader, interface[], InvocationHandler)
方法生成代理类实例。
这个方法用于返回指定接口的代理类实例,该接口将方法调用分配给指定的调用处理程序。
参数Parameters:
1.ClassLoader
:类加载器;
2.interfaces[]
:增强方法所在类的接口列表,支持多接口;
3.InvocationHandler
:需要实现这个接口,创建代理对象,写增强部分。
package com.atguigu.spring5;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
//匿名类创建方式
// Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// return null;
// }
// })
UserDaoImpl userDao = new UserDaoImpl();
UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
int result = dao.add(1, 2);
System.out.println("result: " + result);
}
}
//有名创建代理类实现InvocationHandler接口
class UserDaoProxy implements InvocationHandler {
// 1 创建哪个接口实现类的代理对象,就把谁传递进来
private Object obj;
//使用有参构造函数传递
public UserDaoProxy(Object obj) {
this.obj = obj;
}
// 增强逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
// 方法之前
System.out.println("Before running method: " + method.getName() + ". parameters: " + Arrays.toString(args));
// 被增强的方法执行
Object res = method.invoke(obj, args);
// 方法之后
System.out.println("After method finished: " + obj);
return res;
}
}
动态增强成功
AOP术语
1.连接点
类里哪些方法可以被增强,这些方法称为连接点。
2.切入点
实际被真正增强的方法,称为切入点。
3.通知(增强)
实际增强的逻辑部分成为通知(增强)。
通知类型:
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
4.切面
将通知应用至切入点的过程。
AspectJ注解
开启AspectJ通过注解方式生成代理对象。
在配置文件中,开启注解扫描<context:component-scan>
、Aspect
<?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"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>
<!-- 开启Aspect生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
新建User.class
类,通过@Component
注解建立bean实例对象(相当于<bean id='user' class='包类路径'></bean>
);
@Component
public class User {
public void add() {
System.out.println("add......");
}
}
新建代理类UserProxy.class
,通过@Component
新建bean实例对象,再添加@Aspect
注解生成代理对象;
通知注解配合切入点表达式进行类的增强。
通过切入点表达式,知晓对哪个类的哪个方法进行增强。
语法:execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
例1:对com.atguigu.dao.BookDao
类中的add
方法进行增强
execution(* com.atguigu.dao.BookDao.add(..)
例2:对com.atguigu.dao.BookDao
类中的所有方法进行增强
execution(* com.atguigu.dao.BookDao.*(..)
@Component
@Aspect//生成代理对象
public class UserProxy {
//前置通知注解,配合切入点表达式对User类的add方法进行增强。
@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void before() {
System.out.println("before.....");
}
}
最后测试代理类是否完成增强
public class TestAop {
@Test
public void testAopAnno() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
User user = context.getBean("user", User.class);
user.add();
}
}
通知类型注解有:
@Before
前置通知@After
最终通知,无论是否有异常都会通知@AfterThrowing
异常通知,如有异常这就是最后的通知,后两个通知都不会执行了;@AfterReturning
后置通知(返回通知)@Around
环绕通知
相同切入点抽取
我们上述使用切入点表达式execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
来进行选择切入点,如果多个通知共用同样的切入点,可以对切入点进行一个抽取:@Pointcut(value = "execution(* 切入点)")
注释在代理类的一个方法上,这个方法名就是抽取出的切入点。
例,我们对之前的User.add()
方法这个切入点进行抽取
UserProxy.class
@Component
@Aspect//生成代理对象
public class UserProxy {
//抽取切入点
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//使用@Pointcut注释的方法名
@Before(value = "pointdemo()")
public void before() {
System.out.println("before.....");
}
}
增强类优先级
多个增强类对同一个方法进行了增强,可以通过在代理类上添加注解@Order(数字)
,数字越小优先级越高。
例,我们新建代理类PersonProxy.class
,也对User.add()
方法进行增强,这时有两个代理类对这个方法进行增强了,我们通过@Order(数字)
进行定义优先级。
PersonProxy.class
@Component
@Aspect
@Order(1)
public class PersonProxy {
@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add())")
public void afterRetuning() {
System.out.println("Person Before...");
}
}
UserProxy.class
@Component
@Aspect//生成代理对象
@Order(3)
public class UserProxy {
//抽取切入点
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
@Before(value = "pointdemo()")
public void before() {
System.out.println("before.....");
}
}
测试:
JdbcTemplate
概念
Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库操作。
如何引用
引入依赖包
Spring配置文件中,配置数据库连接池。並配置JdbcTemplate对象,注入DataSource
bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<!-- 数据库为localhost:3306/user_db -->
<property name="url" value="jdbc:mysql://localhost:3306/user_db" />
<property name="username" value="root" />
<property name="password" value='root' />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<!-- JDBCTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入DataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
JdbcTemplate操作数据库
在dao
包进行数据库的操作,使用JdbcTemplate
对象里面update
方法实现添加各种对数据库操作。
格式为:update(String sql, Object... args)
参数:
1.sql
为sql语句,语句中的具体可以用?
代替;
2.args
为参数数组,用来设置sql语句中的具体值;
添加
我们通过JdbcTemplate操作数据库进行添加操作。
首先在工程下建立如下结构:
entity
包存储实体类Book
这里要注意,实体类中的属性信息要和数据库中的信息对应,不然会查不出数据库中的数据,并且属性要设定set与get方法。
package com.atguigu.spring5.entity;
public class Book {
private String book_id;
private String book_name;
private String book_status;
public void setBook_id(String book_id) {
this.book_id = book_id;
}
public void setBook_name(String book_name) {
this.book_name = book_name;
}
public void setBook_status(String book_status) {
this.book_status = book_status;
}
public String getBook_id() {
return book_id;
}
public String getBook_name() {
return book_name;
}
public String getBook_status() {
return book_status;
}
@Override
public String toString() {
return "Book{" +
"bookId='" + book_id + '\'' +
", BookName='" + book_name + '\'' +
", BookStatus='" + book_status + '\'' +
'}';
}
}
dao
包下存储对数据库操作
BookDao.java
package com.atguigu.spring5.dao;
import com.atguigu.spring5.entity.Book;
public interface BookDao {
void add(Book book);
}
BookDaoImpl.java
package com.atguigu.spring5.dao;
import com.atguigu.spring5.entity.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class BookDaoImpl implements BookDao {
// 注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void add(Book book) {
String sql = "insert into t_book value(?, ?, ?)";
// 两种写法
// int update = jdbcTemplate.update(sql, book.getBookId(), book.getBookName(), book.getBookStatus());
Object[] args = {book.getBook_id(), book.getBook_name(), book.getBook_status()};
int update = jdbcTemplate.update(sql, args);
System.out.println(update);
}
@Override
public void updateBook(Book book) {
String sql = "update t_book set book_name = ?, book_status = ? where book_id = ?";
Object[] args = {book.getBook_name(), book.getBook_status(), book.getBook_id()};
int update = jdbcTemplate.update(sql, args);
System.out.println(update);
}
@Override
public void delete(String id) {
String sql = "delete from t_book where book_id = ?";
int update = jdbcTemplate.update(sql, id);
System.out.println(update);
}
@Override
public int selectCount() {
String sql = "select count(*) from t_book";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
@Override
public Book findBookInfo(String id) {
String sql = "select * from t_book where book_name = ?";
// System.out.println(book.toString());
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), "java");
}
@Override
public List<Book> findAllBook() {
String sql = "select * from t_book";
List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));
return bookList;
}
}
service
包
BookService.java
package com.atguigu.spring5.service;
import com.atguigu.spring5.dao.BookDao;
import com.atguigu.spring5.entity.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookService {
// 注入BookDaoImpl
@Autowired
private BookDao bookDao;
public void addBook(Book book) {
bookDao.add(book);
}
}
测试包Test
TestBook.java
package com.atguigu.spring5.test;
import com.atguigu.spring5.entity.Book;
import com.atguigu.spring5.service.BookService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBook {
@Test
public void testJdbcTemplate() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = context.getBean("bookService", BookService.class);
Book book = new Book();
book.setBook_id("1");
book.setBook_name("java");
book.setBook_status("a");
bookService.addBook(book);
}
}
运行测试类,得到反馈。
更新、删除操作
对数据库的更新、删除操作同理,先在dao
包中加入具体的对数据库的操作。
BookDao.java
public interface BookDao {
void add(Book book);
void updateBook(Book book);
void delete(String id);
}
BookDaoImpl.java
public class BookDaoImpl implements BookDao {
// 注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
...
@Override
public void updateBook(Book book) {
String sql = "update t_book set username = ?, ustatus = ? where user_id = ?";
Object[] args = {book.getBookName(), book.getBookStatus(), book.getBookId()};
int update = jdbcTemplate.update(sql, args);
System.out.println(update);
}
@Override
public void delete(String id) {
String sql = "delete from t_book where user_id = ?";
int update = jdbcTemplate.update(sql, id);
System.out.println(update);
}
在service
包中添加操作。
package com.atguigu.spring5.service;
...
@Service
public class BookService {
// 注入dao
@Autowired
private BookDao bookDao;
...
public void updateBook(Book book) {
bookDao.updateBook(book);
}
public void deleteBookById(String id) {
bookDao.delete(id);
}
}
查找并返回对象
使用queryForObject(String sql, RowMapper<T> rowMapper, Object... args);
1.sql:sql语句;
2.RowMapper:接口,针对返回不同类型的数据完成数据封装;
3.sql语句值;
package com.atguigu.spring5.dao;
@Repository
public class BookDaoImpl implements BookDao {
...
@Override
public Book findBookInfo(String id) {
String sql = "select * from t_book where book_name = ?";
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), "java");
return book;
}
}
查找并返回对象集合
和返回对象一样,只是将返回的对象类型变成了List<对象类>
。
package com.atguigu.spring5.dao;
@Repository
public class BookDaoImpl implements BookDao {
...
@Override
public List<Book> findAllBook() {
String sql = "select * from t_book";
List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));
return bookList;
}
}
批量操作
批量操作,比如批量向数据库添加、修改、删除数据,都是通过JdbcTemplate.batchUpdate(String sql, List<Object[]> batchArgs);
方法
1.sql:sql语句;
2.List集合,存储多条要操作的记录;
批量添加、修改、删除
BookDao.java
package com.atguigu.spring5.dao;
public interface BookDao {
void batchAddBook(List<Object[]> batchArgs);
void batchUpdateBook(List<Object[]> batchArgs);
void batchDeleteBook(List<Object[]> batchArgs);
}
BookDaoImpl.java
package com.atguigu.spring5.dao;
@Repository
public class BookDaoImpl implements BookDao {
@Override
public void batchAddBook(List<Object[]> batchArgs) {
String sql = "insert into t_book value(?, ?, ?)";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
@Override
public void batchUpdateBook(List<Object[]> batchArgs) {
String sql = "update t_book set book_name = ?, book_status = ? where book_id = ?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
@Override
public void batchDeleteBook(List<Object[]> batchArgs) {
String sql = "delete from t_book where book_id = ?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
}
BookService.java
package com.atguigu.spring5.service;
@Service
public class BookService {
// 批量添加
public void batchAdd(List<Object[]> batchArgs) {
bookDao.batchAddBook(batchArgs);
}
// 批量修改
public void batchUpdateBook(List<Object[]> batchArgs) {
bookDao.batchUpdateBook(batchArgs);
}
// 批量删除
public void batchDeleteBook(List<Object[]> batchArgs) {
bookDao.batchDeleteBook(batchArgs);
}
}
静态代理
- 静态代理角色分析
抽象角色 : 一般使用接口或者抽象类来实现
真实角色 : 被代理的角色
代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
客户 : 使用代理角色来进行一些操作 .
- 实例:
抽象角色:UserService接口,用户业务,抽象起来就是增删改查
package com.kuang.demo02;
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
真实角色:核心功能UserServiceImpl,真实对象来完成这些增删改查操作
package com.kuang.demo02;
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("更新了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
代理角色:UserServiceProxy,需求来了,现在我们需要增加一个日志功能,怎么实现!
-
思路1 :在实现类上增加代码 【麻烦!】
-
思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能就是最好的了!
package com.kuang.demo02;
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
public void log(String msg) {
System.out.println("执行了" + msg + "方法:");
}
}
测试访问类
public class Client {
public static void main(String[] args) {
//真实业务
UserServiceImpl userService = new UserServiceImpl();
//代理类
UserServiceProxy proxy = new UserServiceProxy();
//使用代理类实现日志功能!
proxy.setUserService(userService);
proxy.add();
}
}
动态代理
JDK的动态代理需要了解两个核心类: InvocationHandler 和 Proxy
动态代理类ProxyInvocationHandler.class:
target是要传入的真实业务
package com.kuang.demo04;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// proxy : 代理类,method : 代理类的调用处理程序的方法对象.
// 处理代理实例上的方法调用并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
//新增加的业务,输出日志
public void log(String methodName) {
System.out.println("执行了" + methodName + "方法");
}
}
测试类Test.class
有了动态代理类,我们可以不用单独建立代理类,可以直接通过ProxyInvocationHandler设定抽象对象来动态的获取代理对象。
package com.kuang.demo04;
import com.kuang.demo02.UserService;
import com.kuang.demo02.UserServiceImpl;
public class Test {
public static void main(String[] args) {
/*真实对象*/
UserServiceImpl userService = new UserServiceImpl();
/*代理对象的调用处理程序*/
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService); //设置要代理的对象
UserService proxy = (UserService)pih.getProxy();//动态生成代理类!
proxy.delete();
}
}