知识点历程

1.spring中dao、controller、service.DAO 是数据访问对象,负责与数据库或其他持久性存储进行交互。Service层主要用于业务逻辑,通常调用 DAO 来获取或存储数据。Controller层用于处理来自用户的请求,并返回响应,通常是 Web 应用中的一部分。
对controller层的映象:用来干两件事,处理前端来自用户的请求和返回响应。如何创建controller层?就是创建controller类,使用@RestController注解。然后 使用@Autowired注解进行依赖注入,@Autowired实际上起到调用Service层(业务逻辑层)中Bean容器中的数据。在Controller(控制层)中如何调用Service(业务逻辑层)则是下一条。这是Controller层做的事。
2.在Controller(控制层)中如何调用Service(业务逻辑层):控制层到底是通过什么来去访问业务逻辑层的,就是使用注解@Autowired来去调用的,但是它并不是去直接调用的,它是去调用业务逻辑层储存在Bean容器中的方法,哪为什么不直接去调用业务逻辑层中实体类的方法呢?这个其实上面也给出了答案,在开发软件时我们所遵守的原则是高内聚低耦合,如果去直接调用的话就会违背了低耦合的原则,所以需要去使用Bean容器进行存储与调用,这样子就避免了耦合。
.
Service层的映象:对从数据库中所拿到的数据进行逻辑处理,再将处理完之后的数据存储在Bean容器中,依旧是这样可有有效的避免耦合性。如何创建Service?首先创建一个接口,接口内定义方法,接着用一个由@Service注解标记的类实现这个接口,其中@Service注解是用来,将此类中的方法及属性都存放在Bean容器中。然后创建属性并使用@Autowired注解标识,获取到数据访问层(DAO)中的数据(调用方式也是从Bean容器中调用的)。最后在类中重写接口中的方法。
3.重写和重载:重写是指在子类中重新定义父类中已经存在的方法。重写特点:* 方法签名相同(名称、参数类型和数量一致)。* 子类方法可以访问父类的方法(使用 super 关键字)。* 主要用于实现多态 重载:重载是指在同一个类中定义多个方法,名称相同但参数列表不同。重载特点:* 方法名称相同,但参数不同。* 返回类型可以相同或不同,但仅凭返回类型不能重载。* 常用于提供不同的输入选项给用户
4.持久层 指与数据存储和访问相关的部分,其主要目标是将数据持久化,即将数据存储在数据库中并能够在应用程序重启后仍然可用。主要组件:实体类、持久化框架、DAO模式。 常见的技术和工具有 JDBC(Java Database Connectivity)、Hibernate、JPA(Java Persistence API)、MyBatis、Spring Data。
5.多态:分为编译时多态(方法重载)和运行时多态(方法重写)。编译时多态是通过方法重载来实现,可以定义多个同名但参数不同的方法。运行时多态是通过方法重写来实现,子类可以重写父类的方法,允许对象的实际类型决定使用哪个方法。
6.@Test 主要用于 Java 编程中的单元测试,尤其是在使用 JUnit 测试框架时。

点击查看代码
import org.junit.jupiter.api.Test; // 使用 JUnit 5 的 @Test 注解
7.接口 映象:接口是一种重要的抽象类型(在这回答了什么是接口),定义一个接口,然后其他类实现这个接口,类中必须对接口中的方法提供自己的实现 8.Java数据结构,如集合等 9.Mybatis Mybatis是一款持久层架构,用于简化JDBC的开发。它简化了数据库操作,使开发者能够执行 SQL 语句而无需频繁编写 JDBC 代码。可以选择使用 XML 文件或注解来定义映射和配置。 10.Stream流 流是个抽象的概念,Java程序中,主要是说io流,在Java IO流中提供了两个转换流:InputStreamReader 和 OutputStreamWriter,这两个类都属于字符流。其中InputStreamReader将字节输入流转为字符输入流,继承自Reader。OutputStreamWriter是将字符输出流转为字节输出流,继承自Writer。具体代码实现:[代码](https://www.cnblogs.com/tanghaorong/p/12363660.html)
点击查看代码
//创建流对象,默认编码方式UTF-8
            isr = new InputStreamReader(new FileInputStream("D:\\IO\\utf8.txt"));
11.Java集合 12.对象序列化 13.java在复合赋值表达式中会记住原始值(Java在计算 i+=expr 时,会记住 i 的原始值),i=12,i+=i-=i*=i 结果输出-120 原因:i+=i-144 12+=12-144 14.事务 15、JDBC
点击查看代码
import java.sql.Connection;  
import java.sql.DriverManager;  
import java.sql.SQLException;  

public class JdbcExample {  
    public static void main(String[] args) {  
        String url = "jdbc:mysql://localhost:3306/mydatabase"; // 数据库URL  
        String user = "root"; // 数据库用户名  
        String password = "password"; // 数据库密码  
        
        Connection connection = null;  
        
        try {  
            // 注册JDBC驱动  
            Class.forName("com.mysql.cj.jdbc.Driver");  
            // 获取数据库连接  
            connection = DriverManager.getConnection(url, user, password);  
            System.out.println("连接成功!");  
        } catch (ClassNotFoundException | SQLException e) {  
            e.printStackTrace();  
        } finally {  
            if (connection != null) {  
                try {  
                    connection.close(); // 关闭连接  
                } catch (SQLException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
}  
16.泛型 17.创建对象的几种方法 * new关键字 * 调用反射 Class.newInstance()(已过时) `Class clazz = Class.forName("com.example.Person"); Person person = (Person) clazz.newInstance(); // 只能调用无参构造` * 调用反射 Constructor.newInstance()(推荐) `Constructor constructor = Person.class.getConstructor(String.class); Person person = constructor.newInstance("Bob");` * 克隆(Clone) 原理 :基于现有对象复制生成新对象,需实现 Cloneable 接口并重写 clone() 方法。 `public class Person implements Cloneable { private String name; @Override public Person clone() throws CloneNotSupportedException { return (Person) super.clone(); } }

Person original = new Person("Charlie");
Person copy = original.clone();`

  • 反序列化 原理: 先序列化将对象转化为字节流存储到文件中。将序列化的字节流还原为对象,需类实现 Serializable 接口。
    `public class Person implements Serializable {
    private String name;
    // 构造方法、getter/setter 省略
    }

// 序列化对象到文件
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
oos.writeObject(new Person("David"));
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person restoredPerson = (Person) ois.readObject();
}`
18.动态代理 Java的动态代理允许在运行时创建实现某个或某些接口的对象,而无需提前编写实现类。它通过java.lang.reflect.Proxy类实现,特别适合用于拦截方法调用、AOP、远程方法调用(RPC)、Mock测试等场景。 动态代理主要分为JDK原生动态代理 和 CGLIB 动态代理。代理模式分为 3 个组成部分:抽象主题对象、真实主题对象、代理主题对象。

Proxy.newProxyInstance():核心方法,用于生成代理实例。三个参数:类加载器(ClassLoader)、代理实现的接口数组、InvocationHandler:定义方法调用时的行为。

点击查看代码
import java.lang.reflect.*;  

interface Hello {  
    void sayHello();  
}  

public class ProxyDemo {  
    public static void main(String[] args) {  
        // 创建一个动态代理实例  ,生成代理对象的核心代码!!!!
        Hello proxyInstance = (Hello) Proxy.newProxyInstance(  
            // 指定类加载器  
            Hello.class.getClassLoader(),  
            // 指定实现的接口列表(这里只有一个:Hello)  
            new Class<?>[]{Hello.class},  
            // 实现InvocationHandler的对象,定义调用方法时的行为  
            new InvocationHandler() {  
                @Override  
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
                    // 在调用任何方法时,会进入这里  
                    if (method.getName().equals("sayHello")) {  
                        System.out.println("Hello, dynamically created proxy!");  
                    }  
                    return null; // 方法没有返回值,返回null  
                }  
            }  
        );  

        // 调用代理对象的方法  
        proxyInstance.sayHello(); // 输出:Hello, dynamically created proxy!  
    }  
}  
基本流程: * 调用Proxy.newProxyInstance(),传入接口、类加载器、处理器。 * 得到一个代理对象(实现了指定接口)。 * 调用该代理对象的方法时,只会调用InvocationHandler.invoke(),你可以在这里定义逻辑。 * 无需显式实现接口的类 JDK原生动态代理的组成分为三个部分: 抽象主题角色 、 真实主题角色 、 增强主题角色 。 真实主题角色和代理模式的真实主题角色一样,都是被代理类;对于JDK原生动态代理而言,抽象主题角色就是接口;增强主题角色在JDK原生代理中值的是实现了 InvocationHandler 接口的类,其目的是对真实主题角色的方法的增强。编写客户端其实就是生成代理对象,并通过代理对象来调用InvocationHandler接口中invoke里的方法和实现或调用真实主题角色类(被代理类)的方法。执行顺序主要看InvocationHandler类中invoke方法。如:
点击查看代码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 真实方法执行前的增强  ,在这里是执行本类中方法
        before(method.getName());
        // 真实方法执行  ,在这里执行真实类(被代理类)的方法
        Object result = method.invoke(target, args);
        after(method.getName());
        // 真实方法执行后的增强
        return result;
    }

遇到的问题:先描述本项目的切面是怎么做的?在代理中Object result = method.invoke(target, args); 中这两个参数是什么,为什么要加入,在重写invoke方法时,invoke方法里为什么有method.invoke 答:在method.invoke(target, args)中:target参数,这是被代理的原始对象(目标对象),例如:OrderServiceImpl的实例 为什么要加入:因为我们需要调用原始对象的方法,而不是代理对象的方法,否则会造成无限递归 proxy: 代理对象本身 method: 被调用的方法 args: 方法参数 方法调用链: ` // 1. 客户端调用代理对象的方法 proxy.createOrder("ORD001", "张三", 1000.0);

// 2. 代理对象调用InvocationHandler的invoke方法
// 3. invoke方法中通过method.invoke调用目标对象的方法
method.invoke(target, args);执行流程: // 客户端代码
OrderService proxy = AopProxyHandler.createProxy(new OrderServiceImpl());
proxy.createOrder("ORD001", "张三", 1000.0);

// 实际执行流程

  1. proxy.createOrder()被调用
  2. 触发InvocationHandler.invoke()
  3. 在invoke中:
    • 执行前置增强(日志记录)
    • 调用原始方法:method.invoke(target, args)
    • 执行后置增强(性能监控)
  4. 返回结果给客户端简单了解:为什么需要这种设计? 解耦:* 业务逻辑和横切关注点(日志、性能监控)分离 * 可以独立修改和扩展各个切面 动态性:* 在运行时动态创建代理 * 可以根据需要添加或移除切面 灵活性:* 可以针对不同的方法应用不同的切面 * 通过注解控制切面的应用范围 可维护性:* 切面逻辑集中管理 * 便于统一修改和维护 问题又来了:因为我们需要调用原始对象的方法,而不是代理对象的方法,否则会造成无限递归。 什么是代理对象的方法,什么是原始对象的方法,没明白,在本项目中指的是? 答:在本项目中: 原始对象(Target Object): 就是OrderServiceImpl的实例,也就是new OrderServiceImpl()创建的对象,这个对象包含了实际的业务逻辑实现。 代理对象(Proxy Object): 通过Proxy.newProxyInstance()创建的对象,也就是AopProxyHandler.createProxy(target)返回的对象,这个对象是动态生成的,实现了与原始对象相同的接口(OrderService)。 为什么会发生无限递归?假设在invoke方法中,我们错误地调用了代理对象而非原始对象的方法:// 错误的实现 - 会导致无限递归
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 这里的proxy是代理对象自身
// 假设我们错误地使用proxy调用方法
Object result = method.invoke(proxy, args); // 错误!会导致无限递归

return result;

}`
递归过程:

  • 客户端调用proxy.createOrder()

  • 代理拦截该调用,触发invoke方法

  • 在invoke内部,我们又调用proxy.createOrder()

  • 这又会触发invoke方法

  • 继续循环,导致无限递归

  • 最终导致StackOverflowError
    正确做法:
    `// 正确的实现
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // target是原始对象(OrderServiceImpl实例)
    Object result = method.invoke(target, args); // 正确!调用原始对象的方法

    return result;
    }`

posted @ 2025-04-18 20:27  哈孜  阅读(24)  评论(0)    收藏  举报