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{@AroundInvokepublic Object computeLatency(InvocationContext invocationCtx) throws Exception{long startTime = System.currentTimeMillis();//execute the intercepted method and store the return valueObject 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 {@InjectPeopleDAO peopleDAO;@LatencyLoggerpublic List<Person> getAllPeople() throws SQLException {return peopleDAO.getAllPeople();}@LatencyLoggerpublic 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 {@InjectPeopleService peopleService;@Overridepublic 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>
这样,您将可以使用Interceptor构建一个非常简单的应用程序。 感谢您的阅读,并一直与我在一起。 请分享您的查询/反馈作为评论。 并在您的朋友之间分享这篇文章!
翻译自: https://www.javacodegeeks.com/2014/09/simple-aspect-oriented-programming-aop-using-cdi-in-javaee.html
javaee编程题