知识点历程
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 注解
点击查看代码
//创建流对象,默认编码方式UTF-8
isr = new InputStreamReader(new FileInputStream("D:\\IO\\utf8.txt"));
点击查看代码
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();
}
}
}
}
}
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!
}
}
点击查看代码
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;
}
// 2. 代理对象调用InvocationHandler的invoke方法
// 3. invoke方法中通过method.invoke调用目标对象的方法
method.invoke(target, args);执行流程: // 客户端代码
OrderService proxy = AopProxyHandler.createProxy(new OrderServiceImpl());
proxy.createOrder("ORD001", "张三", 1000.0);
// 实际执行流程
- proxy.createOrder()被调用
- 触发InvocationHandler.invoke()
- 在invoke中:
- 执行前置增强(日志记录)
- 调用原始方法:method.invoke(target, args)
- 执行后置增强(性能监控)
- 返回结果给客户端
简单了解:为什么需要这种设计? 解耦:* 业务逻辑和横切关注点(日志、性能监控)分离 * 可以独立修改和扩展各个切面 动态性:* 在运行时动态创建代理 * 可以根据需要添加或移除切面 灵活性:* 可以针对不同的方法应用不同的切面 * 通过注解控制切面的应用范围 可维护性:* 切面逻辑集中管理 * 便于统一修改和维护 问题又来了:因为我们需要调用原始对象的方法,而不是代理对象的方法,否则会造成无限递归。 什么是代理对象的方法,什么是原始对象的方法,没明白,在本项目中指的是? 答:在本项目中: 原始对象(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;
}`

浙公网安备 33010602011771号