1. 使用注解开发
1. 说明
(1)在Spring4之后,想要使用注解的形式,必须先引入aop包
(2)在配置文件中,引入context约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
2. Bean的实现
(1)Bean注入:@Component
- 指定注解的扫描包
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!--指定注解扫描包-->
<context:component-scan base-package="com.nelws"/>
</beans>
- 在指定包下编写类,增加注解
@Component //相当于配置文件中 <bean id="user" class="com.nelws.domain.User"/>
public class User {
public String name = "老王";
}
- 测试
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user", User.class);
System.out.println(user.name); //老王
}
(2)属性注入:@Value
- 配置实体类方式1
@Component //相当于配置文件中 <bean id="user" class="com.nelws.domain.User"/>
public class User {
@Value("小王") //相当于配置文件中 <property name="name" value="小王"/>
public String name;
}
- 测试1
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user", User.class);
System.out.println(user.name); //小王
}
- 配置实体类方式2
@Component //相当于配置文件中 <bean id="user" class="com.nelws.domain.User"/>
public class User {
public String name;
@Value("王王王") //相当于配置文件中 <property name="name" value="小王"/>
public void setName(String name) {
this.name = name;
}
}
- 测试2
@Test
public void test03(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user", User.class);
System.out.println(user.name); //王王王
}
(3)@Component的三个衍生注解
为了更好地进行分层,Spring可以使用其他三个注解,功能一样
@Controller:web层
@Service:service层
@Repository:dao层
(4)作用域:@Scope
singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂,所有的对象都会被销毁
prototype:多例模式。关闭工厂,所有的对象不会被销毁。内部的垃圾回收机制会回收
@Component //相当于配置文件中 <bean id="user" class="com.nelws.domain.User"/>
@Scope("singleton")
public class User {
public String name;
@Value("王王王") //相当于配置文件中 <property name="name" value="小王"/>
public void setName(String name) {
this.name = name;
}
}
3. 总结
(1)XML与注解比较
XML可以适用任何场景 ,结构清晰,维护方便
注解不是自己提供的类使用不了,开发简单方便
(2)XML与注解整合开发
xml管理Bean
注解完成属性注入
使用过程中, 可以不用扫描,扫描是为了类上的注解
4. 基于Java类进行配置
(1)说明:JavaConfig原来是Spring的一个子项目,它通过Java类的方式提供Bean的定义信息,在 Spring4的版本,JavaConfig已正式成为Spring4的核心功能
(2)测试
- 实体类Dog
@Component
public class Dog {
public String name = "旺财";
}
- 新建一个config配置包,编写一个MyConfig的配置类
@Configuration //表明这是一个配置类,类似之前的beans.xml,这个也会被Spring容器托管,注入到容器中
@ComponentScan("com.nelws.domain")
public class MyConfig {
/**
* 注册一个Bean,就相当于之前写的一个bean标签
* 这个方法的名字,就相当于bean标签中的id
* 这个方法的返回值,就相当于bean标签的class
*/
@Bean
public Dog dog(){
return new Dog(); //这个就是返回要注入到bean的对象
}
}
- 测试
@Test
public void test04(){
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
Dog dog = context.getBean("dog", Dog.class);
System.out.println(dog.name); //旺财
}
(3)导入其他配置:@Import
@Configuration //代表这是一个配置类
public class MyConfig2 {
}
@Configuration
@Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {
@Bean
public Dog dog(){
return new Dog();
}
}
2. 静态/动态代理
1. 学习代理模式的原因:Spring的AOP机制的底层就是动态代理
2. 代理模式
3. 静态代理
(1)静态代理的角色分析
抽象角色:一般使用接口或者抽象类来实现
真实角色:被代理的角色
代理角色:代理真实角色;代理真实角色后,一般会做一些附属操作
客户:使用代理角色来进行操作
(2)测试
- Rent(抽象角色)
/**
* 抽象角色:租房
*/
public interface Rent {
public void rent();
}
- Host(真实角色)
/**
* 真实角色:房东
* 房东要租房子
*/
public class Host implements Rent{
public void rent() {
System.out.println("房东要出租房屋!");
}
}
- Proxy(代理角色)
/**
* 代理角色:中介
*/
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
//租房
public void rent() {
seeHouse();
host.rent();
fare();
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
- Client(客户)
/**
* 客户:一般客户都会去找代理
*/
public class Client {
public static void main(String[] args) {
//房东要租房
Host host = new Host();
//中介帮助房东
Proxy proxy = new Proxy(host);
//客户去找中介
proxy.rent();
/**
* 带房客看房
* 房东要出租房屋!
* 收中介费
*/
}
}
分析:在这个过程中,客户直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式
(3)静态代理的优缺点
优点
可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情
公共的业务由代理来完成,实现了业务的分工
公共业务发生扩展时变得更加集中和方便
缺点
类多了,多了代理类,工作量变大了,开发效率降低
4. 静态代理的深入理解
(1)环境搭建
- 创建一个抽象角色,比如我们平时做的用户业务,抽象起来就是增删改查
/**
* 抽象角色:完成增删改查业务
*/
public interface UserService {
void add();
void delete();
void update();
void query();
}
- 我们需要一个真实的对象来完成这些增删改查的操作
/**
* 真实角色:完成增删改查操作的人
*/
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加一个用户");
}
public void delete() {
System.out.println("删除一个用户");
}
public void update() {
System.out.println("修改一个用户");
}
public void query() {
System.out.println("查询一个用户");
}
}
- 测试
/**
* 客户
*/
public class Client {
public static void main(String[] args) {
UserService service = new UserServiceImpl();
service.add(); //增加一个用户
service.delete(); //删除一个用户
}
}
(2)突然增加了一个需求:用在调用方法时增加一个日志功能。我们要使用代理模式去做,可以再不影响原来业务的情况下,实现此功能
- 创建一个抽象角色,比如我们平时做的用户业务,抽象起来就是增删改查
/**
* 抽象角色:完成增删改查业务
*/
public interface UserService {
void add();
void delete();
void update();
void query();
}
- 我们需要一个真实的对象来完成这些增删改查的操作
/**
* 真实角色:完成增删改查操作的人
*/
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加一个用户");
}
public void delete() {
System.out.println("删除一个用户");
}
public void update() {
System.out.println("修改一个用户");
}
public void query() {
System.out.println("查询一个用户");
}
}
- 现在设置一个代理角色,来完成处理日志的功能
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
showLog("add");
userService.add();
}
public void delete() {
showLog("delete");
userService.delete();
}
public void update() {
showLog("update");
userService.update();
}
public void query() {
showLog("query");
userService.query();
}
public void showLog(String log){
System.out.println("[Debug] 日志~" + log + "~执行了");
}
}
- 测试
public class Client {
public static void main(String[] args) {
//真实业务
UserServiceImpl service = new UserServiceImpl();
//代理类
UserServiceProxy proxy = new UserServiceProxy();
//使用代理类提供的日志功能
proxy.setUserService(service);
proxy.add();
proxy.delete();
/**
* [Debug] 日志~add~执行了
* 增加一个用户
* [Debug] 日志~delete~执行了
* 删除一个用户
*/
}
}
结论:我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
5. 动态代理
(1)说明:动态代理的角色和静态代理的角色是一样的,区别是动态代理的代理类是动态生成的,静态代理的代理类是写死的
(2)动态代理的分类
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理----cglib
(3)现在用的比较多的是javassist来生成动态代理(JDK动态代理)
(4)JDK动态代理核心的两个类:InvocationHandler和Proxy
- InvocationHandler【调用处理程序】
Object invoke(Object proxy, Method method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
- Proxy【代理】
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
(5)测试
- Rent(抽象角色)
/**
* 抽象角色:租房
*/
public interface Rent {
public void rent();
}
- Host(真实角色)
/**
* 真实角色:房东
*/
public class Host implements Rent{
public void rent() {
System.out.println("房东要租房子!");
}
}
- ProxyInvocationHandler(代理角色)
/**
* 代理角色:中介
* 我们就用这个类,自动生成代理类
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成代理类,重点是第二个参数,获取要代理的抽象角色
//之前都是一个角色,现在可以代理一类角色
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
//proxy:代理类
//method:代理类的调用处理程序的方法对象
//处理代理示例上的方法调用并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//核心:利用反射实现
Object result = method.invoke(rent, args);
fare();
return result;
}
public void seeHouse(){
System.out.println("带房客看房");
}
public void fare(){
System.out.println("收中介费");
}
}
- Client(客户)
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理实例的调用
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host); //将真实角色放进去
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
/**
* 带房客看房
* 房东要租房子!
* 收中介费
*/
}
}
核心:一个动态代理,一般代理某一类业务,一个动态代理可以代理多个类,代理的是接口!
6. 动态代理深化理解
(1)使用动态代理来实现之前的UserService
(2)测试
- UserService(抽象角色)
/**
* 抽象角色:完成增删改查业务
*/
public interface UserService {
void add();
void delete();
void update();
void query();
}
- UserServiceImpl(真实角色)
/**
* 真实角色:完成增删改查操作的人
*/
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加一个用户");
}
public void delete() {
System.out.println("删除一个用户");
}
public void update() {
System.out.println("修改一个用户");
}
public void query() {
System.out.println("查询一个用户");
}
}
- ProxyInvocationHandler(代理类):我们可以编写一个通用的代理类
public class ProxyInvocatinHandler implements InvocationHandler {
private Object target;
public void setObject(Object object) {
this.target = object;
}
//生成代理的类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
getLog(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void getLog(String methodName){
System.out.println("[Debug] 执行了~" + methodName + "~方法");
}
}
- Client(客户)
public class Client {
public static void main(String[] args) {
//真实对象
UserServiceImpl userService = new UserServiceImpl();
//代理对象的调用处理程序
ProxyInvocatinHandler pih = new ProxyInvocatinHandler();
pih.setObject(userService); //设置代理对象
UserService proxy = (UserService) pih.getProxy(); //生成动态代理类
proxy.add();
proxy.delete();
/**
* [Debug] 执行了~add~方法
* 增加一个用户
* [Debug] 执行了~delete~方法
* 删除一个用户
*/
}
}
7. 总结:动态代理的好处
(1)可以使真实角色的操作更加纯粹,不用去关注一些公共业务
(2)公共业务交给代理角色,实现了业务的分工
(3)公共业务发生扩展的时候,方便集中管理
(4)一个动态代理类代理一个接口,一般就是对应的一个业务
(5)一个动态代理类可以代理多个类,只要实现了同一个接口即可