【狂神说Java】Spring5最新完整教程IDEA版通俗易懂,面试分享一次成功的经历

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

2-4.UserServiceImpl 业务实现类

public class UserServiceImpl implements UserService {

UserDao userDao = new UserDaoImpl();

public void getUser() {

userDao.getUser();

}

}

2-5.测试

public class Test {

public static void main(String[] args) {

UserService userService = new UserServiceImpl();

userService.getUser();

}

}

在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改一次的成本代价十分昂贵!

在这里插入图片描述

我们使用一个Set接口实现,已经发生了革命性的变化!

UserDao userDao;

public void setUserDao(UserDao userDao){

this.userDao = userDao;

}

之前,程序是主动创建对象!控制权在程序员手上!

使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象!

控制反转了,主动权交给用户了

在这里插入图片描述

这种思想,从本质上解决了问题,我们程序猿不用再去管理对象的创建了。系统的耦合性大大降低~,可以更加专注的在业务的实现上!这是IOC的原型!


3、IOC本质


在这里插入图片描述

在这里插入图片描述

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。


4、Spring 入门程序


4-1.实体类

public class User {

private String name;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public String toString() {

return “User{” +

“name='” + name + ‘’’ +

‘}’;

}

}

4-2.配置文件

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

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

4-3.测试

public static void main(String[] args) {

//获取Spring 的上下文对象

ApplicationContext ac = new ClassPathXmlApplicationContext(“beans.xml”);

//我们的对象现在都在Spring 中管理了,我们要使用,直接去里面取出来就行了

User user = (User)ac.getBean(“user”);

//打印

System.out.println(user.toString());

}

4-4.结果

User{name=‘闲言’}

4-5.思考问题?

Hello对象是谁创建的?

Hello对象是由Spring创建的。

Hello对象的属性是怎么设置的?

Hello对象的属性是由Spring容器设置的。

这个过程就叫控制反转:

控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。

反转:程序本身不创建对象,而变成被动的接收对象。

依赖注入:就是利用set方法来进行注入的。

IOC是一种编程思想,由主动的编程变成被动的接收。

可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。

到了现在,不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配!

在这里插入图片描述


5、IOC创建对象的方式


5-1.使用无参构造创建对象,默认!

public User() {

System.out.println(“User 空参构造方法执行了”);

}

5-2.使用有参构造创建对象

下标赋值

public User(String name) {

this.name = name;

}

5-3.类型

public User(String name) {

this.name = name;

}

参数名

在这里插入图片描述

总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!

Spring 容器,就类似于婚介网站。

  • 信息都注册在里面

  • 你想查看(获取)的时候再拿


6、Spring配置


6-1.别名

ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);

User user = (User)ac.getBean(“user2”);

user.show();

输出:csnd_闲言

6-2.Bean的配置

别名可以通过很多种方式分割

空格分割

逗号分割

分号分割

// User user = (User)ac.getBean(“user”);

// User user = (User)ac.getBean(“user2”);

// User user = (User)ac.getBean(“u2”);

// User user = (User)ac.getBean(“u3”);

User user = (User)ac.getBean(“u4”);

user.show();

输出:csdn_闲言

6-3.import

这个import。一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。

假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!

  1. 张三

  2. 李四

  3. 王五

在这里插入图片描述

使用的时候,直接使用总的配置就可以了。


7、依赖注入


7-1.构造器注入

7-2.Set 注入

依赖:bean对象的创建依赖于容器!

注入:bean对象中的所有属性,由容器来注入!

Address类

public class Address {

private String address;

省略setter

}

student类

public class Student {

private String name;

private Address address;

private String[] books;

private List hobbies;

private Map<String,String> card;

private Set games;

private String wife;

private Properties info;

省略setter

}

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”

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

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

JavaSE

JavaWeb

Spring

SpringMVC

Mybatis

rap

篮球

rap

篮球

v1

v2

v3

测试

public static void main(String[] args) {

ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);

Student student = (Student)ac.getBean(“student”);

System.out.println(student);

}

输出

Student{

name=‘csdn_闲言’,

address=Address{address=‘csdn_闲言’},

books=[JavaSE, JavaWeb, Spring, SpringMVC, Mybatis],

hobbies=[唱, 跳, rap, 篮球],

card={闲言=csdn——闲言, 闲言博客=csdn——闲言——博客},

games=[唱, 跳, rap, 篮球],

wife=‘xxx’,

info={p3=v3, p2=v2, p1=v1}

}


## 8、拓展方式注入 可以使用**p命名空间**和**c命名空间**进行注入

  1. p 命名空间对应 setter 方式注入(要提供set方法)

  2. c 命令空间对应 构造方法 (要提供有参构造方法)

<?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:c=“http://www.springframework.org/schema/c”

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

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

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:p=“http://www.springframework.org/schema/p”

xmlns:c=“http://www.springframework.org/schema/c”

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

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

pojp

public class User {

private String name;

private int age;

public User() {

}

public User(String name, int age) {

this.name = name;

this.age = age;

}

省略setter

}

测试

ApplicationContext ac = new ClassPathXmlApplicationContext(“userbeans.xml”);

User user = (User)ac.getBean(“user”);

User user2 = (User)ac.getBean(“user2”);

System.out.println(user);

System.out.println(user2);

输出

User{name=‘闲言’, age=18}

User{name=‘csdn——闲言’, age=19}


9、bean的作用域


在这里插入图片描述

在这里插入图片描述

9-1.单例模式(Spring 默认机制)

scope=“singleton”

User user2 = (User)ac.getBean(“user2”);

User user3 = (User)ac.getBean(“user2”);

System.out.println(user2.hashCode());

System.out.println(user3.hashCode());

输出

817406040

817406040

9-2.原型模式:每次从容器中get的时候,都会产生一个新对象

ApplicationContext ac = new ClassPathXmlApplicationContext(“userbeans.xml”);

for (int i = 0;i< 5;i++){

System.out.println(ac.getBean(“user”).hashCode());

}

输出

817406040

1955915048

1270855946

2083117811

157683534

单线程—单例

多线程—多例


10.Bean的自动装配


自动装配是Spring 满足bean依赖的一种方式!

Spring 会在上下文自动寻找,并自动给bean 装配属性!

在Spring 中有三种装配的方式

  1. 在xml 中显示的配置

  2. 在java中显示配置

  3. 隐式 的自动装配bean【重要】

ByName方法自动装配

  1. autowire=“byName”

  2. 会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id!

  3. 弊端:set 方法后面的值和 id 相同

bean.xml

测试

public static void main(String[] args) {

ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);

People people = ac.getBean(“people”, People.class);

people.getCat().shout();

people.getDog().shout();

}

ByName方法自动装配

  1. autowire=“byType”

  2. 会自动在容器上下文中查找,和自己对象属性类型相同的bean

  3. 弊端:它必须保证类型全局唯一(在IOC容器中只能由一个)。

测试

public static void main(String[] args) {

ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);

People people = ac.getBean(“people”, People.class);

people.getCat().shout();

people.getDog().shout();

}

总结:

byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致

byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致


11、注解实现自动装配


  1. jdk1.5支持的注解 Spring2.5支持的注解

  2. The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML

  3. @Autowired

  4. @Quelifier

  5. @Resource

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

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

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

context:annotation-config/

11-1.@Autowired

  1. 在属性上使用

  2. 在set方式上使用

  3. 使用Autowired 可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在(需要通过其他方式注入进容器),且符合名字byName。

public class People {

@Autowired

private Dog dog;

@Autowired

private Cat cat;

private String name;

public People() {

}

public People(Dog dog, Cat cat, String name) {

this.dog = dog;

this.cat = cat;

this.name = name;

}

省略setter

}

Autowired 有一个个唯一的属性( required )

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Autowired {

boolean required() default true;

}

@Nullable

字段标记了这个注解,表示这个字段可以为null

@Qualifier

  • 当我们的容器存在多个相同类型,不同名称的bean。使用@Autowired 无法完成自动装配了

  • 这个时候需要使用@Qualifier 和@Autowired 注解一起使用。

  • 使用@Qualifier 指定一个唯一的bean对象注入!

例如

IOC容器中存在多个 相同类型不同id的bean

在这里插入图片描述

通过@Qualifier 指定一个唯一的bean

在这里插入图片描述

测试

在这里插入图片描述

在这里插入图片描述


11-2.@Resource

不指定name值,先去判断byNamebyType,有一个能注入即成功

根据类型查找

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

11-3.根据类型和id查找

如果IOC容器中存在多个不同名称,相同类型的bean,可以通过@Resource注解的name属性指定唯一的id;

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

总结:

  1. 都是用来自动装配的,都可以放在*属性字段*上

  2. @Autowired通过byType(类型)的方式实现,而且必须要求这个对象存在

  3. @Resource默认通过byName(id)的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!

  4. 执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byName的方式实现。


12、使用注解开发


在Spring4之后,要使用注解开发,必须保证aop的包导入了

在这里插入图片描述

使用注解需要导入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”

xmlns:aop=“http://www.springframework.org/schema/aop”

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

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

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

<context:component-scan base-package=“cn.bloghut”/>

context:annotation-config/

bean注入使用**@Componet**注解

等价<bean id="user" class="cn.bloghut.domain.User">

@Component

public class User {

}

属性注入使用@Value注解

@Component

public class User {

private String name;

@Value(“闲言”)

public void setName(String name){

this.name = name;

}

}

衍生注解

  • @Componet有几个衍生注解,在web开发中,会按照mvc三层架构分层!

  • @Service--------业务层注解

  • @Repository—持久层注解

  • @Controller-----控制层注解

这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean

自动装配

  • @Autowired 自动装配通过类型、名字

如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value=“xxx”)

  • @Nullable 字段标记了这个注解,说明这个字段可以为null

  • @Resource 自动装配通过名字,类型

作用域

@Scope(“singleton”)单例

在这里插入图片描述

总结

XML 与 注解

  • xml更加万能,适用于任何场合!维护简单方便

  • 注解不是自己类使用不了, 维护相对复杂

XML 与 注解最佳实践

  • xml用来管理bean

  • 注解只负责完成属性的注入

  • 我们在使用过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持

<context:component-scan base-package=“cn.bloghut”/>

context:annotation-config/


13、java的方式配置Spring


JavaConfig 是Spring的 一个子项目,在Spring 4之后,它成为了新功能

在这里插入图片描述

  1. 首先定义一个类,在类上添加@Component注解,让它加载到Spring IOC容器(让Spring 管理)

  2. 定义Java 配置类,在其类上添加@Configuration 注解,说明该类是一个配置类,这个类也会被Spring 托管,因为它本身是一个@Component

  3. 在Myconfig 类中 添加getUser 方法,返回一个user对象

  4. getUser 方法上的@Bean 注解 则是注册一个bean功能,相当于

  1. 方法的名字,相当于bean 标签中的id 属性。

  2. 方法的返回值,相当于bean 标签中的class属性。(因为我们导包了,Spring 知道是哪一个)

  3. 如果完全使用了配置类方式去做,我们只能通过AnnotationConfigApplicationContext 上下文来获取容器,通过配置类的class 对象加载!

在这里插入图片描述

完整代码

  • @Configuration 这个一个配置类

  • @ComponentScan 用于扫描包

  • @Import 用于导入其他配置类

13-1.主配置类

@Configuration

@ComponentScan(basePackages = {“cn.bloghut.domain”})

@Import({MyConfig2.class})

public class MyConfig {

/**

  • 返回一个User Bean

  • @return

*/

@Bean

public User getUser(){

return new User();

}

}

13-2.配置类2

@Configuration

public class MyConfig2 {

}

13-3.User实体类

@Component

public class User {

private String name;

public String getName() {

return name;

}

@Value(“闲言”)

public void setName(String name) {

this.name = name;

}

@Override

public String toString() {

return “User{” +

“name='” + name + ‘’’ +

‘}’;

}

}

13-4.测试类

public class MyTest {

public static void main(String[] args) {

ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);

User user = ac.getBean(“getUser”, User.class);

System.out.println(user);

}

}


14、代理模式


14-1.角色分析:

  1. 抽象角色:一般会使用接口或者抽象类来解决

  2. 真实角色:被代理的角色

  3. 代理角色:代理 真实角色,代理真实角色后,我们一般会做一些附属操作

  4. 客户:访问代理对象的人

代码步骤:

14-2.接口

public interface Rent {

/**

  • 租房

*/

void rent();

}

14-3.真实角色

public class Host implements Rent{

@Override

public void rent() {

System.out.println(“房东要出租房子”);

}

}

14-4.代理角色

public class Proxy implements Rent{

private Host host;

public Proxy() {

}

public Proxy(Host host) {

this.host = host;

}

@Override

public void rent() {

seeHouse();

host.rent();

fare();

hetong();

}

//看房

public void seeHouse(){

System.out.println(“中介带你看房”);

}

//收中介费

public void fare(){

System.out.println(“收中介费”);

}

//签合同

public void hetong(){

System.out.println(“签合同”);

}

}

14-5.客户端访问代理角色

public class Client {

public static void main(String[] args) {

Host host = new Host();

//代理,中介帮房东,但是呢?代理角色一般会有一些附属操作!

Proxy proxy = new Proxy(host);

//你不用面对房东,直接找中介租房即可!

proxy.rent();

}

}

14-6.代理模式的好处:

  1. 可以使真实角色的操作更加存粹!不用去关注一些公共的业务

  2. 公共交给了代理角色,实现了业务的分工

  3. 公共业务发生扩展的时候,方便集中管理

14-7.缺点:

  • 一个真实角色就会产生一个代理角色,代码量会翻倍 开发效率变低

在这里插入图片描述

在这里插入图片描述


15、静态代理


  1. 有一天,公司领导要求我为 某个类的所有方法新增日志输出功能。

  2. 怎么做呢?

  3. 在原来的类上的每一个方法添加日志输出?

  4. 这就是改变原来人家的代码了。

  5. 改动原有的业务代码,在公司中是大忌!

  6. 有可能人家你修改了人家的代码,可能给改蹦了。

  7. 新增一个类,制作成本小,职责单一。

原来的开发方式(纵向开发)

在这里插入图片描述

添加日志功能(横切进去)

在这里插入图片描述

代码如下:

业务接口:

public interface UserService {

void add();

void delete();

void update();

void query();

}

原来业务类

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(“查询用户”);

}

}

代理类

public class UserServiceProxy implements UserService {

private UserService userService;

public void setUserService(UserService 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() {

System.out.println(“query”);

userService.query();

}

//日志方法

public void log(String msg){

System.out.println(“【Debug 】使用了”+msg+“方法”);

}

}

测试类

public static void main(String[] args) {

UserServiceProxy userService = new UserServiceProxy();

userService.setUserService(new UserServiceImpl());

userService.add();

}


16、动态代理


  • 动态代理和静态代理角色一样

  • 动态代理的代理类是动态生成的,不是我们直接写好的

动态代理分为两大类:基于接口的动态代理,基于类的动态代理

  • 基于接口:JDK动态代理

  • 基于类: cglib

  • java字节码实现: javasist

需要了解两个类:Proxy :代理 InvocationHandler:调用处理程序

java.lang.reflect.Proxy

Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。(大白话:这是一个静态类,类里边有方法得到代理类)

动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口。 代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,它实现了接口

通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例, java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组。 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。

代理类具有以下属性:

  1. 代理类是公共的,最终的,而不是抽象的,如果所有代理接口都是公共的。

  2. 如果任何代理接口是非公开的,代理类是非公开的,最终的,而不是抽象的 。

  3. 代理类的不合格名称未指定。 然而,以字符串"$Proxy"开头的类名空间应该保留给代理类。

  4. 一个代理类扩展了java.lang.reflect.Proxy 。

  5. 代理类完全按照相同的顺序实现其创建时指定的接口。

  6. 如果一个代理类实现一个非公共接口,那么它将被定义在与该接口相同的包中。 否则,代理类的包也是未指定的。 请注意,程序包密封不会阻止在运行时在特定程序包中成功定义代理类,并且类也不会由同一类加载器定义,并且与特定签名者具有相同的包。

  7. 由于代理类实现了在其创建时指定的所有接口, getInterfaces在其类对象上调用getInterfaces将返回一个包含相同列表接口的数组(按其创建时指定的顺序),在其类对象上调用getMethods将返回一个数组的方法对象,其中包括这些接口中的所有方法,并调用getMethod将在代理接口中找到可以预期的方法。

  8. Proxy.isProxyClass方法将返回true,如果它通过代理类 - 由Proxy.getProxyClass返回的类或由Proxy.newProxyInstance返回的对象的类 - 否则为false。

  9. 所述java.security.ProtectionDomain代理类的是相同由引导类装载程序装载系统类,如java.lang.Object ,因为是由受信任的系统代码生成代理类的代码。 此保护域通常将被授予java.security.AllPermission 。

  10. 每个代理类有一个公共构造一个参数,该接口的实现InvocationHandler ,设置调用处理程序的代理实例。 而不必使用反射API来访问公共构造函数,也可以通过调用Proxy.newProxyInstance方法来创建代理实例,该方法将调用Proxy.getProxyClass的操作与调用处理程序一起调用构造函数。


17、基于Proxy类和InvocationHandler 实现动态代理


17-1.真实的角色

public class Host implements Rent{

public void rent() {

System.out.println(“租房”);

}

}

17-2.被代理的接口


public interface Rent {

void rent();

}

17-3.代理 真实的角色 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);

}

/**

  • 处理代理实例,并返回结果

  • @param proxy

  • @param method

  • @param args

  • @return

  • @throws Throwable

*/

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

//动态代理的本质,就是使用反射机制

//在方法调用前调用

seeHouse();

Object result = method.invoke(rent,args);

//在方法调用后调用

heTong();

return result;

}

public void seeHouse(){

System.out.println(“看房子”);

}

public void heTong(){

System.out.println(“签合同”);

}

}

17-4.用户

public class Client {

public static void main(String[] args) {

//创建真实角色

Host host = new Host();

//创建代理角色 不存在

ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();

//设置要代理的对象

proxyInvocationHandler.setRent(host);

//获取代理对象,并强制转换

Rent proxy = (Rent)proxyInvocationHandler.getProxy();

//调用

proxy.rent();

}

}

17-5.输出结果

看房子

租房

签合同


18、基于Proxy类和InvocationHandler 实现动态代理 2


Proxy:生成动态代理实例的

InvocationHandler:调用处理程序并返回结果的

万能的自动生成代理类

package cn.bloghut.demo3;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

/**

  • @Classname ProxyInvocationHandler

  • @Description 自动生成代理类

  • @Date 2021/5/17 15:44

  • @Created by 闲言

*/

public class ProxyInvocationHandler implements InvocationHandler {

//1.被代理的接口

public Object target;

public void setTarget(Object target){

this.target = target;

}

//2.生成得到代理类(代理谁)

public Object getProxy(){

return Proxy.newProxyInstance(this.getClass().getClassLoader(),

target.getClass().getInterfaces(),this);

}

//3.处理代理实例,并返回结果(代用代理程序)

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

//动态获取方法名称

log(method.getName());

Object result = method.invoke(target, args);

return result;

}

/**

  • 打印日志方法

  • @param msg

*/

public void log(String msg){

System.out.println(“[debug]===> 执行可”+msg+“方法”);

}

}

public static void main(String[] args) {

//真实角色

UserServiceImpl userService = new UserServiceImpl();

//代理角色,不存在

ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler();

//设置要代理的对象

invocationHandler.setTarget(userService);

//获取代理对象

UserService proxy = (UserService)invocationHandler.getProxy();

//执行方法

proxy.query();

}

[debug]===> 执行可query方法

query


19、动态代理和静态代理总结


19-1.静态代理

  1. 由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了

  2. 静态代理通常只代理一个类

  3. 静态代理事先知道要代理的是什么

代码如下:

  1. 如下, HelloServiceProxy类是代理类,HelloServiceImpl类是委托类,这两个类都实现了HelloService接口。

  2. 其中HelloServiceImpl类是HelloService接口的真正实现者,而HelloServiceProxy类是通过调用HelloServiceImpl类的相关方法来提供特定服务的。

  3. HelloServiceProxy类的echo()方法和getTime()方法会分别调用被代理的HelloServiceImpl对象的echo()方法和getTime()方法,并且在方法调用前后都会执行一些简单的打印操作。

  4. 由此可见,代理类可以为委托类预处理消息、把消息转发给委托类和事后处理消息等。

HelloService接口

public interface HelloService {

String echo(String msg);

Date getTime();

}

HelloServiceImpl委托类

public class HelloServiceImpl implements HelloService {

@Override

public String echo(String msg) {

return “echo:” + msg;

}

@Override

public Date getTime() {

return new Date();

}

}

HelloServiceProxy代理类

public class HelloServiceProxy implements HelloService {

//表示被代理的HelloService 实例

private HelloService helloService;

public HelloServiceProxy(HelloService helloService) {

this.helloService = helloService;

}

public void setHelloServiceProxy(HelloService helloService) {

this.helloService = helloService;

}

@Override

public String echo(String msg) {

//预处理

System.out.println(“before calling echo()”);

//调用被代理的HelloService 实例的echo()方法

String result = helloService.echo(msg);

//事后处理

System.out.println(“after calling echo()”);

return result;

}

public Date getTime() {

//预处理

System.out.println(“before calling getTime()”);

//调用被代理的HelloService 实例的getTime()方法

Date date = helloService.getTime();

//事后处理

System.out.println(“after calling getTime()”);

return date;

}

}

测试

public class Test {

public static void main(String[] args) {

//创建委托类

HelloServiceImpl helloService = new HelloServiceImpl();

//创建代理类

HelloServiceProxy helloServiceProxy = new HelloServiceProxy(helloService);

//调用方法

Date time = helloServiceProxy.getTime();

System.out.println(time);

}

}

19-2.动态代理

  1. 在程序运行时,运用反射机制动态创建而成

  2. 动态代理是代理一个接口下的多个实现类

  3. 动态代理不知道要代理什么东西,只有在运行时才知道

与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。

Proxy类提供了创建动态代理类及其实例的静态方法。

代码如下:

public static void main(String[] args) {

//真实角色

HelloServiceImpl helloService = new HelloServiceImpl();

//代理角色 不存在

ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();

proxyInvocationHandler.setTarget(helloService);

HelloService proxy = (HelloService)proxyInvocationHandler.getProxy();

//执行方法

String echo = proxy.echo(“Proxy InvocationHandler 实现动态代理”);

System.out.println(echo);

}

输出

代理方法执行前调用

代理方法执行后调用

echo:Proxy InvocationHandler 实现动态代理

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);

}

//执行代理的方法

@Override

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

System.out.println(“代理方法执行前调用”);

//执行方法

Object result = method.invoke(target, args);

System.out.println(“代理方法执行后调用”);

return result;

}

}

最后

各位读者,由于本篇幅度过长,为了避免影响阅读体验,下面我就大概概括了整理了

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

implements HelloService {

//表示被代理的HelloService 实例

private HelloService helloService;

public HelloServiceProxy(HelloService helloService) {

this.helloService = helloService;

}

public void setHelloServiceProxy(HelloService helloService) {

this.helloService = helloService;

}

@Override

public String echo(String msg) {

//预处理

System.out.println(“before calling echo()”);

//调用被代理的HelloService 实例的echo()方法

String result = helloService.echo(msg);

//事后处理

System.out.println(“after calling echo()”);

return result;

}

public Date getTime() {

//预处理

System.out.println(“before calling getTime()”);

//调用被代理的HelloService 实例的getTime()方法

Date date = helloService.getTime();

//事后处理

System.out.println(“after calling getTime()”);

return date;

}

}

测试

public class Test {

public static void main(String[] args) {

//创建委托类

HelloServiceImpl helloService = new HelloServiceImpl();

//创建代理类

HelloServiceProxy helloServiceProxy = new HelloServiceProxy(helloService);

//调用方法

Date time = helloServiceProxy.getTime();

System.out.println(time);

}

}

19-2.动态代理

  1. 在程序运行时,运用反射机制动态创建而成

  2. 动态代理是代理一个接口下的多个实现类

  3. 动态代理不知道要代理什么东西,只有在运行时才知道

与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。

Proxy类提供了创建动态代理类及其实例的静态方法。

代码如下:

public static void main(String[] args) {

//真实角色

HelloServiceImpl helloService = new HelloServiceImpl();

//代理角色 不存在

ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();

proxyInvocationHandler.setTarget(helloService);

HelloService proxy = (HelloService)proxyInvocationHandler.getProxy();

//执行方法

String echo = proxy.echo(“Proxy InvocationHandler 实现动态代理”);

System.out.println(echo);

}

输出

代理方法执行前调用

代理方法执行后调用

echo:Proxy InvocationHandler 实现动态代理

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);

}

//执行代理的方法

@Override

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

System.out.println(“代理方法执行前调用”);

//执行方法

Object result = method.invoke(target, args);

System.out.println(“代理方法执行后调用”);

return result;

}

}

最后

各位读者,由于本篇幅度过长,为了避免影响阅读体验,下面我就大概概括了整理了

[外链图片转存中…(img-EBZXWBsA-1713668402788)]

[外链图片转存中…(img-hy6Dalpm-1713668402788)]

[外链图片转存中…(img-86zVMsyn-1713668402789)]

[外链图片转存中…(img-vwiZKbNk-1713668402789)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-BOYw16yv-1713668402789)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值