предисловие
В этой статье представлен режим адаптера, анализ исходного кода режима адаптера в spring aop, jpa, mvc.
Рекомендуемое чтение
Шаблоны проектирования | Простые фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны фабричных методов и типичные приложения
Шаблоны проектирования | Абстрактные фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны построителей и типичные приложения
Шаблоны проектирования | Шаблоны прототипов и типовые приложения
Шаблоны проектирования | Шаблоны внешнего вида и типичные приложения
Шаблоны проектирования | Шаблоны декораторов и типичные приложения
Больше материалов можно найти в моем личном блоге:laijianfeng.org
Обратите внимание на общедоступную учетную запись [Xiao Xuanfeng] WeChat и своевременно получайте сообщения в блогах.
режим адаптера
Шаблон адаптера: Преобразование интерфейса в другой интерфейс, который хочет клиент, чтобы эти классы с несовместимыми интерфейсами могли работать вместе, и его псевдоним — Wrapper. Шаблон адаптера может использоваться либо как шаблон структуры класса, либо как объектно-структурный шаблон.
В шаблоне адаптера мы решаем проблему несовместимости интерфейсов, добавляя новый класс адаптера, чтобы классы, не имеющие отношения, могли работать вместе.
В зависимости от отношения между классом адаптера и классом адаптера режим адаптера можно разделить на два типа: адаптер объекта и адаптер класса.Шаблон адаптера объекта, между переходником и переходником естьассоциацияотношения; вшаблон адаптера класса, между переходником и переходником естьнаследовать(или реализация) отношения.
Роль
Target (целевой абстрактный класс): целевой абстрактный класс определяет интерфейс, необходимый клиенту, который может быть абстрактным классом или интерфейсом, или конкретным классом.
Адаптер (класс адаптера): адаптер может вызывать другой интерфейс в качестве преобразователя для адаптации Adaptee и Target. Класс адаптера является ядром режима адаптера. В объектном адаптере он соединяет их, наследуя Target и связывая объект Adaptee.
Adaptee (класс адаптера): Адаптер — это роль, которую необходимо адаптировать. Он определяет существующий интерфейс. Этот интерфейс необходимо адаптировать. Класс адаптера обычно представляет собой конкретный класс, содержащий бизнес-методы, которые клиент хочет использовать. В некоторых случаях может не быть исходный код класса адаптера.
Шаблон адаптера по умолчанию: если вам не нужно реализовывать все методы, предоставляемые интерфейсом, вы можете сначала разработать абстрактный класс для реализации интерфейса и предоставить реализацию по умолчанию (пустой метод) для каждого метода в интерфейсе, а затем подклассы абстрактный класс может выборочно переопределять некоторые методы родительского класса для достижения требований.Это подходит для ситуаций, когда вы не хотите использовать все методы в интерфейсе, также известном как шаблон адаптера с одним интерфейсом. Режим адаптера по умолчанию — это вариант режима адаптера, который также широко используется. Режим адаптера по умолчанию широко используется в пакете обработки событий java.awt.event библиотеки классов JDK, например WindowAdapter, KeyAdapter, MouseAdapter и т. д.
Пример
адаптер класса
Сначала есть существующий класс, который будет адаптирован
public class Adaptee {
public void adapteeRequest() {
System.out.println("被适配者的方法");
}
}
определить целевой интерфейс
public interface Target {
void request();
}
Как это может быть в целевом интерфейсеrequest()
перечислитьAdaptee
изadapteeRequest()
метод?
Если непосредственно реализоватьTarget
это невозможно
public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("concreteTarget目标方法");
}
}
Если через класс адаптера, реализацияTarget
интерфейс, наследуяAdaptee
класс, а затем реализоватьrequest()
метод, вызывающий родительский классadapteeRequest()
может быть достигнут
public class Adapter extends Adaptee implements Target{
@Override
public void request() {
//...一些操作...
super.adapteeRequest();
//...一些操作...
}
}
Давайте проверим это
public class Test {
public static void main(String[] args) {
Target target = new ConcreteTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
вывод
concreteTarget目标方法
被适配者的方法
Таким образом, мы можем использовать новый интерфейсTarget
Адаптация старых интерфейсов или классов в
объектный адаптер
Разница между адаптером объекта и адаптером класса заключается в том, что адаптер класса завершает адаптацию посредством наследования, а адаптер объекта завершает адаптацию посредством ассоциации, которая здесь немного изменена.Adapter
класс можно превратить в объектный адаптер
public class Adapter implements Target{
// 适配者是对象适配器的一个属性
private Adaptee adaptee = new Adaptee();
@Override
public void request() {
//...
adaptee.adapteeRequest();
//...
}
}
Обратите внимание здесьAdapter
будетAdaptee
как свойство члена вместо его наследования
адаптер напряжения
Другой простой для понимания пример: гражданское электричество в нашей стране составляет 220 В, в Японии — 110 В, а нашему мобильному телефону обычно требуется 5 В для зарядки, в настоящее время для зарядки нам нужен адаптер напряжения для преобразования входного напряжения 220 В или 100 В в выход 5В
Определите выходной интерфейс переменного тока, выход 220 В переменного тока и выход 110 В переменного тока.
public interface AC {
int outputAC();
}
public class AC110 implements AC {
public final int output = 110;
@Override
public int outputAC() {
return output;
}
}
public class AC220 implements AC {
public final int output = 220;
@Override
public int outputAC() {
return output;
}
}
интерфейс адаптера, гдеsupport()
метод используется для проверки соответствия входного напряжения адаптеру,outputDC5V()
Метод используется для преобразования входного напряжения в 5В и выходного
public interface DC5Adapter {
boolean support(AC ac);
int outputDC5V(AC ac);
}
Реализуйте китайский адаптер трансформатора и японский адаптер трансформатора
public class ChinaPowerAdapter implements DC5Adapter {
public static final int voltage = 220;
@Override
public boolean support(AC ac) {
return (voltage == ac.outputAC());
}
@Override
public int outputDC5V(AC ac) {
int adapterInput = ac.outputAC();
//变压器...
int adapterOutput = adapterInput / 44;
System.out.println("使用ChinaPowerAdapter变压适配器,输入AC:" + adapterInput + "V" + ",输出DC:" + adapterOutput + "V");
return adapterOutput;
}
}
public class JapanPowerAdapter implements DC5Adapter {
public static final int voltage = 110;
@Override
public boolean support(AC ac) {
return (voltage == ac.outputAC());
}
@Override
public int outputDC5V(AC ac) {
int adapterInput = ac.outputAC();
//变压器...
int adapterOutput = adapterInput / 22;
System.out.println("使用JapanPowerAdapter变压适配器,输入AC:" + adapterInput + "V" + ",输出DC:" + adapterOutput + "V");
return adapterOutput;
}
}
Протестируйте, подготовьте китайский адаптер трансформатора и японский адаптер трансформатора, определите метод поиска подходящего трансформатора в зависимости от напряжения, а затем протестируйте
public class Test {
private List<DC5Adapter> adapters = new LinkedList<DC5Adapter>();
public Test() {
this.adapters.add(new ChinaPowerAdapter());
this.adapters.add(new JapanPowerAdapter());
}
// 根据电压找合适的变压器
public DC5Adapter getPowerAdapter(AC ac) {
DC5Adapter adapter = null;
for (DC5Adapter ad : this.adapters) {
if (ad.support(ac)) {
adapter = ad;
break;
}
}
if (adapter == null){
throw new IllegalArgumentException("没有找到合适的变压适配器");
}
return adapter;
}
public static void main(String[] args) {
Test test = new Test();
AC chinaAC = new AC220();
DC5Adapter adapter = test.getPowerAdapter(chinaAC);
adapter.outputDC5V(chinaAC);
// 去日本旅游,电压是 110V
AC japanAC = new AC110();
adapter = test.getPowerAdapter(japanAC);
adapter.outputDC5V(japanAC);
}
}
вывод
使用ChinaPowerAdapter变压适配器,输入AC:220V,输出DC:5V
使用JapanPowerAdapter变压适配器,输入AC:110V,输出DC:5V
Сводка шаблонов адаптеров
главное преимущество:
- Отделите целевой класс от класса адаптера и повторно используйте существующий класс адаптера, введя класс адаптера без изменения исходной структуры.
- Это повышает прозрачность и возможность повторного использования класса, а также инкапсулирует конкретный бизнес-процесс реализации в классе адаптера, который прозрачен для класса клиента, и улучшает возможность повторного использования адаптера.Один и тот же адаптер Класс оператора можно повторно использовать во многих различных системы.
- Гибкость и расширяемость очень хорошие, с помощью конфигурационного файла можно легко заменить адаптер и добавить новые классы адаптера без изменения исходного кода, что полностью соответствует принципу «открыто-закрыто».
В частности, шаблон адаптера класса имеет следующие преимущества:
- Поскольку класс адаптера является подклассом класса адаптера, некоторые методы адаптера могут быть заменены в классе адаптера, что делает адаптер более гибким.
Шаблон адаптера объекта также имеет следующие преимущества:
- Адаптер объекта может адаптировать несколько разных адаптеров к одной и той же цели;
- Подкласс адаптера может быть адаптирован.Поскольку адаптер и адаптер связаны, в соответствии с «принципом замещения Рихтера», подкласс адаптера также может быть адаптирован через адаптер.
Недостатки шаблона адаптера класса заключаются в следующем:
- Для таких языков, как Java и C#, которые не поддерживают множественное наследование классов, за один раз можно адаптировать не более одного класса адаптера, а несколько адаптеров нельзя адаптировать одновременно;
- Класс адаптера не может быть окончательным классом, например окончательным классом в Java и запечатанным классом в C#;
- В таких языках, как Java и C#, целевой абстрактный класс в шаблоне адаптера класса может быть только интерфейсом, а не классом, и его использование имеет определенные ограничения.
Недостатки шаблона адаптера объекта заключаются в следующем:
- По сравнению с шаблоном адаптера класса замена некоторых методов класса адаптера в адаптере является более громоздкой. Если вам необходимо заменить один или несколько методов класса адаптера, вы можете сначала создать подкласс класса адаптера, заменить методы класса адаптера, а затем рассматривать подкласс класса адаптера как реальный. процесс реализации усложняется.
Применимая сцена:
- Системе нужно использовать какие-то существующие классы, а интерфейсы этих классов (например, имена методов) не удовлетворяют потребности системы, и даже нет исходного кода этих классов.
- Хотите создать многоразовый класс для работы с некоторыми классами, которые не очень связаны друг с другом, в том числе с теми, которые могут быть введены в будущем.
Типичное применение шаблона адаптера анализа исходного кода
Шаблон адаптера в Spring AOP
В Spring Aop, используяAdvice(通知)
для расширения функциональности проксируемого класса.
Advice
Типы:MethodBeforeAdvice
,AfterReturningAdvice
,ThrowsAdvice
в каждом типеAdvice
Есть соответствующие перехватчики.MethodBeforeAdviceInterceptor
,AfterReturningAdviceInterceptor
,ThrowsAdviceInterceptor
Spring необходимо преобразовать каждыйAdvice
Все инкапсулировано в соответствующий тип перехватчика, возвращается в контейнер, поэтому вам нужно использовать режим адаптера дляAdvice
для преобразования
Три класса адаптеров Adaptee следующие:
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable;
}
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(@Nullable Object var1, Method var2, Object[] var3, @Nullable Object var4) throws Throwable;
}
public interface ThrowsAdvice extends AfterAdvice {
}
Целевой интерфейс Target имеет два метода, один для сужденияAdvice
Независимо от того, соответствует ли тип, один из них является фабричным методом, который создает соответствующий тип.Advice
Соответствующий перехватчик
public interface AdvisorAdapter {
boolean supportsAdvice(Advice var1);
MethodInterceptor getInterceptor(Advisor var1);
}
Три класса адаптеров Adapter следующие: обратите внимание на соответствие между Advice, Adapter и Interceptor.
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
@SuppressWarnings("serial")
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof AfterReturningAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
}
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof ThrowsAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
return new ThrowsAdviceInterceptor(advisor.getAdvice());
}
}
Client DefaultAdvisorAdapterRegistry
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
private final List<AdvisorAdapter> adapters = new ArrayList(3);
public DefaultAdvisorAdapterRegistry() {
// 这里注册了适配器
this.registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
this.registerAdvisorAdapter(new AfterReturningAdviceAdapter());
this.registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor)advice);
}
Iterator var4 = this.adapters.iterator();
while(var4.hasNext()) {
AdvisorAdapter adapter = (AdvisorAdapter)var4.next();
if (adapter.supportsAdvice(advice)) { // 这里调用适配器方法
interceptors.add(adapter.getInterceptor(advisor)); // 这里调用适配器方法
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
} else {
return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[0]);
}
}
// ...省略...
}
Вот посмотрите на цикл while, вытащите зарегистрированные адаптеры один за другим, вызовитеsupportsAdvice()
метод судитьAdvice
соответствующий тип, а затем вызовитеgetInterceptor()
Создайте соответствующий тип перехватчика
Это должно принадлежать шаблону объектного адаптера, ключевому словуinstanceof
можно рассматривать какAdvice
метод, но вотAdvice
Объекты передаются извне, а не свойства членов
Шаблон адаптера в Spring JPA
В пакете Spring ORM режим адаптера также используется для поддержки JPA Во-первых, определяется интерфейс.JpaVendorAdapter
, а затем этот интерфейс реализуют различные платформы уровня сохраняемости.
jpaVendorAdapter: используется для установки определенных свойств, которые реализуют реализацию JPA поставщика, например свойство generateDdl, которое устанавливает, будет ли Hibernate автоматически генерировать DDL; эти свойства зависят от поставщика, поэтому лучше всего установить их здесь; в настоящее время Spring предоставляетHibernateJpaVendorAdapter
,OpenJpaVendorAdapter
,EclipseLinkJpaVendorAdapter
,TopLinkJpaVendorAdapter
Четыре реализации. Наиболее важным атрибутом является база данных, которая используется для указания типа используемой базы данных, чтобы она моглаВ зависимости от типа базы данных, например, как преобразовать исключения, специфичные для базы данных, в согласованные исключения Spring., в настоящее время поддерживает следующие базы данных (DB2, DERBY, H2, HSQL, INFORMIX, MYSQL, ORACLE, POSTGRESQL, SQL_SERVER, SYBASE)
public interface JpaVendorAdapter
{
// 返回一个具体的持久层提供者
public abstract PersistenceProvider getPersistenceProvider();
// 返回持久层提供者的包名
public abstract String getPersistenceProviderRootPackage();
// 返回持久层提供者的属性
public abstract Map<String, ?> getJpaPropertyMap();
// 返回JpaDialect
public abstract JpaDialect getJpaDialect();
// 返回持久层管理器工厂
public abstract Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface();
// 返回持久层管理器
public abstract Class<? extends EntityManager> getEntityManagerInterface();
// 自定义回调方法
public abstract void postProcessEntityManagerFactory(EntityManagerFactory paramEntityManagerFactory);
}
Давайте посмотрим на один из классов реализации адаптера, HibernateJpaVendorAdapter.
public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
//设定持久层提供者
private final PersistenceProvider persistenceProvider;
//设定持久层方言
private final JpaDialect jpaDialect;
public HibernateJpaVendorAdapter() {
this.persistenceProvider = new HibernatePersistence();
this.jpaDialect = new HibernateJpaDialect();
}
//返回持久层方言
public PersistenceProvider getPersistenceProvider() {
return this.persistenceProvider;
}
//返回持久层提供者
public String getPersistenceProviderRootPackage() {
return "org.hibernate";
}
//返回JPA的属性
public Map<String, Object> getJpaPropertyMap() {
Map jpaProperties = new HashMap();
if (getDatabasePlatform() != null) {
jpaProperties.put("hibernate.dialect", getDatabasePlatform());
} else if (getDatabase() != null) {
Class databaseDialectClass = determineDatabaseDialectClass(getDatabase());
if (databaseDialectClass != null) {
jpaProperties.put("hibernate.dialect",
databaseDialectClass.getName());
}
}
if (isGenerateDdl()) {
jpaProperties.put("hibernate.hbm2ddl.auto", "update");
}
if (isShowSql()) {
jpaProperties.put("hibernate.show_sql", "true");
}
return jpaProperties;
}
//设定数据库
protected Class determineDatabaseDialectClass(Database database)
{
switch (1.$SwitchMap$org$springframework$orm$jpa$vendor$Database[database.ordinal()])
{
case 1:
return DB2Dialect.class;
case 2:
return DerbyDialect.class;
case 3:
return H2Dialect.class;
case 4:
return HSQLDialect.class;
case 5:
return InformixDialect.class;
case 6:
return MySQLDialect.class;
case 7:
return Oracle9iDialect.class;
case 8:
return PostgreSQLDialect.class;
case 9:
return SQLServerDialect.class;
case 10:
return SybaseDialect.class; }
return null;
}
//返回JPA方言
public JpaDialect getJpaDialect() {
return this.jpaDialect;
}
//返回JPA实体管理器工厂
public Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface() {
return HibernateEntityManagerFactory.class;
}
//返回JPA实体管理器
public Class<? extends EntityManager> getEntityManagerInterface() {
return HibernateEntityManager.class;
}
}
Файл конфигурации можно указать так
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="database" value="HSQL"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
Шаблон адаптера весной MVC
Шаблон адаптера в Spring MVC в основном используется для выполнения целевогоController
Метод обработки запросов в .
В Spring MVC,DispatcherServlet
как пользователь,HandlerAdapter
В качестве ожидаемого интерфейса для адаптации целевого класса используется конкретный класс реализации адаптера.Controller
как класс, который необходимо адаптировать.
Зачем использовать шаблон адаптера в Spring MVC? весной MVCController
много видов, разные видыController
Запросы обрабатываются по-разному. Если вы не используете режим адаптера,DispatcherServlet
Получить соответствующий тип напрямуюController
, вам нужно судить самостоятельно, например, следующий код:
if(mappedHandler.getHandler() instanceof MultiActionController){
((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
...
}else if(...){
...
}
Предположим, что если мы добавимHardController
, вам нужно добавить строку в кодif(mappedHandler.getHandler() instanceof HardController)
, такая форма затрудняет сопровождение программы и нарушает принцип открытости-закрытости в шаблоне проектирования — открыт для расширения, закрыт для модификации.
Давайте взглянем на исходный код, первый — это интерфейс адаптера.HandlerAdapter
public interface HandlerAdapter {
boolean supports(Object var1);
ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
long getLastModified(HttpServletRequest var1, Object var2);
}
Каждый из адаптеров, представляющих этот интерфейсController
Есть соответствующий ему переходник, в данном случае каждый кастомныйController
Необходимо определить реализациюHandlerAdapter
адаптер.
предоставлено в springmvcController
Классы реализации следующие
предоставлено в springmvcHandlerAdapter
Класс реализации выглядит следующим образом
HttpRequestHandlerAdapter
Этот код адаптера выглядит следующим образом
public class HttpRequestHandlerAdapter implements HandlerAdapter {
public HttpRequestHandlerAdapter() {
}
public boolean supports(Object handler) {
return handler instanceof HttpRequestHandler;
}
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler)handler).handleRequest(request, response);
return null;
}
public long getLastModified(HttpServletRequest request, Object handler) {
return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
}
}
Когда контейнер Spring запускается, он сохраняет все определенные объекты адаптера в коллекции List.Когда приходит запрос,DispatcherServlet
пройдетhandler
найти соответствующий адаптер и вернуть объект адаптера пользователю, а затем его можно унифицировать с помощью адаптераhanle()
способ вызоваController
Метод, используемый для обработки запроса в .
public class DispatcherServlet extends FrameworkServlet {
private List<HandlerAdapter> handlerAdapters;
//初始化handlerAdapters
private void initHandlerAdapters(ApplicationContext context) {
//..省略...
}
// 遍历所有的 HandlerAdapters,通过 supports 判断找到匹配的适配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
}
// 分发请求,请求需要找到匹配的适配器来处理
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
// 确定当前请求的匹配的适配器.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
ha.getLastModified(request, mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
// ...省略...
}
С шаблоном адаптера мы ставим всеcontroller
Равномерно переданоHandlerAdapter
обработки, избавляя от необходимости писать многоif-else
пара операторовController
Это также более благоприятно для расширения новыхController
тип.
Ссылаться на:
Лю Вэй: Шаблоны проектирования Java Edition
MOOC Java Design Patterns Интенсивный метод отладки + анализ памяти
Одинокий, уединенный:Шаблон адаптера в Spring MVC
ToughMind_:Шаблоны проектирования простым языком (5): 7. Шаблон адаптера