javaee
我们编写满足特定业务逻辑的服务API。 涵盖所有服务API(如安全性,日志记录,审核,度量延迟等)的跨领域问题很少。 这是一个重复的非业务代码,可以在其他方法之间重用。 重用的一种方法是将这些重复代码移入其自己的方法,并在服务API中调用它们,例如:
public class MyService{
public ServiceModel service1(){
isAuthorized();
//execute business logic.
}
}
public class MyAnotherService{
public ServiceModel service1(){
isAuthorized():
//execute business logic.
}
}
上面的方法会起作用,但不会在不产生代码噪音的情况下起作用,将横切关注点与业务逻辑混合在一起。 有另一种方法可以通过使用Aspect来解决上述要求,这种方法称为面向方面的编程(AOP)。 可以使用AOP的不同方式-通过使用Spring AOP,JavaEE AOP。 在此示例中,我将尝试在Java EE应用程序中使用使用CDI的AOP。 为了解释这一点,我选择了一个非常简单的示例,该示例构建一个Web应用程序以从Database中获取少量记录并显示在浏览器中。
创建数据访问层
表结构为:
create table people(
id INT NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
place varchar(100),
primary key(id));
让我们创建一个Model类来保存个人信息
package demo.model;
public class Person{
private String id;
private String name;
private String place;
public String getId(){ return id; }
public String setId(String id) { this.id = id;}
public String getName(){ return name; }
public String setName(String name) { this.name = name;}
public String getPlace(){ return place; }
public String setPlace(String place) { this.place = place;}
}
让我们创建一个公开两种方法的数据访问对象–
- 获取所有人的细节
- 获取给定ID的一个人的详细信息
package demo.dao;
import demo.common.DatabaseConnectionManager;
import demo.model.Person;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class PeopleDAO {
public List<Person> getAllPeople() throws SQLException {
String SQL = "SELECT * FROM people";
Connection conn = DatabaseConnectionManager.getConnection();
List<Person> people = new ArrayList<>();
try (Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery(SQL)) {
while (rs.next()) {
Person person = new Person();
person.setId(rs.getString("id"));
person.setName(rs.getString("name"));
person.setPlace(rs.getString("place"));
people.add(person);
}
}
return people;
}
public Person getPerson(String id) throws SQLException {
String SQL = "SELECT * FROM people WHERE id = ?";
Connection conn = DatabaseConnectionManager.getConnection();
try (PreparedStatement ps = conn.prepareStatement(SQL)) {
ps.setString(1, id);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
Person person = new Person();
person.setId(rs.getString("id"));
person.setName(rs.getString("name"));
person.setPlace(rs.getString("place"));
return person;
}
}
}
return null;
}
}
您可以使用自己的方法来获取新的连接。 在上面的代码中,我创建了一个静态实用程序,该实用程序返回了相同的连接。
创建拦截器
创建拦截器涉及2个步骤:
- 创建Interceptor绑定,该绑定创建带有
@InterceptorBinding
注释的注释,该注释用于绑定拦截器代码和需要拦截的目标代码。 - 创建一个用
@Interceptor
注释的类,其中包含拦截器代码。 它包含用@AroundInvoke
注释的方法,不同的生命周期注释,@AroundTimeout
等。
让我们通过名称@LatencyLogger
创建一个拦截器绑定
package demo;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;
import javax.interceptor.InterceptorBinding;
@InterceptorBinding
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface LatencyLogger {
}
现在我们需要创建Interceptor代码,该代码以@Interceptor
注释,并以上面创建的Interceptor绑定进行注释,即@LatencyLogger
:
package demo;
import java.io.Serializable;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
@Interceptor
@LatencyLogger
public class LatencyLoggerInterceptor implements Serializable{
@AroundInvoke
public Object computeLatency(InvocationContext invocationCtx) throws Exception{
long startTime = System.currentTimeMillis();
//execute the intercepted method and store the return value
Object returnValue = invocationCtx.proceed();
long endTime = System.currentTimeMillis();
System.out.println("Latency of " + invocationCtx.getMethod().getName() +": " + (endTime-startTime)+"ms");
return returnValue;
}
}
上面的代码中有两个有趣的事情:
- 使用
@AroundInvoke
- 传递给方法的
InvocationContext
类型的参数
@AroundInvoke
将方法指定为拦截器方法。 一个Interceptor类只能有一个带有此注释的方法。 每当目标方法被拦截时,其上下文都会传递给拦截器。 使用InvocationContext
可以获取方法的详细信息,并将参数传递给该方法。
我们需要在WEB-INF / beans.xml文件中声明以上拦截器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
<interceptors>
<class>demo.LatencyLoggerInterceptor</class>
</interceptors>
</beans>
创建带有拦截器注释的服务API
我们已经创建了Interceptor绑定和被执行的拦截器。 现在让我们创建Service API,然后使用Interceptor绑定对其进行注释
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package demo.service;
import demo.LatencyLogger;
import demo.dao.PeopleDAO;
import demo.model.Person;
import java.sql.SQLException;
import java.util.List;
import javax.inject.Inject;
public class PeopleService {
@Inject
PeopleDAO peopleDAO;
@LatencyLogger
public List<Person> getAllPeople() throws SQLException {
return peopleDAO.getAllPeople();
}
@LatencyLogger
public Person getPerson(String id) throws SQLException {
return peopleDAO.getPerson(id);
}
}
我们已经使用Interceptor绑定@LatencyLogger
注释了服务方法。 另一种方法是在类级别进行注释,然后将注释应用于类的所有方法。 还要注意的另一件事是@Inject
批注,该批注注入实例,即将依赖项注入到类中。
接下来是连接Controller和View以显示数据。 控制器是servlet,视图是使用JSTL标记的纯JSP。
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package demo;
import demo.model.Person;
import demo.service.PeopleService;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "AOPDemo", urlPatterns = {"/AOPDemo"})
public class AOPDemoServlet extends HttpServlet {
@Inject
PeopleService peopleService;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
List<Person> people = peopleService.getAllPeople();
Person person = peopleService.getPerson("2");
request.setAttribute("people", people);
request.setAttribute("person", person);
getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
} catch (SQLException ex) {
Logger.getLogger(AOPDemoServlet.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
上面的servlet可在http:// localhost:8080 /获得。
/ AOPDemo。 它获取数据并重定向到视图以显示该数据。 请注意,该服务也已使用@Inject
注释注入。 如果没有注入依赖项,而是使用new
创建依赖项,则拦截器将无法工作。 这是我在构建此样本时意识到的重要一点。
呈现数据的JSP将是
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>AOP Demo</title>
</head>
<body>
<h1>Hello World!</h1>
<table>
<tr>
<th>Id</th>
<th>Name</th>
<th>Place</th>
</tr>
<c:forEach items="${requestScope.people}" var="person">
<tr>
<td><c:out value="${person.id}"/></td>
<td><c:out value="${person.name}"/></td>
<td><c:out value="${person.place}"/></td>
</tr>
</c:forEach>
</table>
<br/>
Details for person with id=2
<c:out value="Name ${person.name} from ${person.place}" />
</body>
</html>
这样,您将可以使用拦截器构建一个非常简单的应用程序。 感谢您的阅读,并一直陪着我到此为止。 请分享您的查询/反馈作为评论。 并在您的朋友之间分享这篇文章!
javaee